diff --git a/monkey/common/cloud/aws_instance.py b/monkey/common/cloud/aws_instance.py
index f80b44474..748bd8d04 100644
--- a/monkey/common/cloud/aws_instance.py
+++ b/monkey/common/cloud/aws_instance.py
@@ -1,22 +1,48 @@
+import json
import re
import urllib2
+import logging
+
__author__ = 'itay.mizeretz'
+AWS_INSTANCE_METADATA_LOCAL_IP_ADDRESS = "169.254.169.254"
+AWS_LATEST_METADATA_URI_PREFIX = 'http://{0}/latest/'.format(AWS_INSTANCE_METADATA_LOCAL_IP_ADDRESS)
+ACCOUNT_ID_KEY = "accountId"
+
+
+logger = logging.getLogger(__name__)
+
class AwsInstance(object):
+ """
+ Class which gives useful information about the current instance you're on.
+ """
+
def __init__(self):
+ self.instance_id = None
+ self.region = None
+ self.account_id = None
+
try:
- self.instance_id = urllib2.urlopen('http://169.254.169.254/latest/meta-data/instance-id', timeout=2).read()
+ self.instance_id = urllib2.urlopen(
+ AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/instance-id', timeout=2).read()
self.region = self._parse_region(
- urllib2.urlopen('http://169.254.169.254/latest/meta-data/placement/availability-zone').read())
- except urllib2.URLError:
- self.instance_id = None
- self.region = None
+ urllib2.urlopen(AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read())
+ except urllib2.URLError as e:
+ logger.error("Failed init of AwsInstance while getting metadata: {}".format(e.message))
+
+ try:
+ self.account_id = self._extract_account_id(
+ urllib2.urlopen(
+ AWS_LATEST_METADATA_URI_PREFIX + 'dynamic/instance-identity/document', timeout=2).read())
+ except urllib2.URLError as e:
+ logger.error("Failed init of AwsInstance while getting dynamic instance data: {}".format(e.message))
@staticmethod
def _parse_region(region_url_response):
- # For a list of regions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html
+ # For a list of regions, see:
+ # https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html
# This regex will find any AWS region format string in the response.
re_phrase = r'((?:us|eu|ap|ca|cn|sa)-[a-z]*-[0-9])'
finding = re.findall(re_phrase, region_url_response, re.IGNORECASE)
@@ -33,3 +59,21 @@ class AwsInstance(object):
def is_aws_instance(self):
return self.instance_id is not None
+
+ @staticmethod
+ def _extract_account_id(instance_identity_document_response):
+ """
+ Extracts the account id from the dynamic/instance-identity/document metadata path.
+ Based on https://forums.aws.amazon.com/message.jspa?messageID=409028 which has a few more solutions,
+ in case Amazon break this mechanism.
+ :param instance_identity_document_response: json returned via the web page ../dynamic/instance-identity/document
+ :return: The account id
+ """
+ return json.loads(instance_identity_document_response)[ACCOUNT_ID_KEY]
+
+ def get_account_id(self):
+ """
+ :return: the AWS account ID which "owns" this instance.
+ See https://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html
+ """
+ return self.account_id
diff --git a/monkey/common/cloud/aws_service.py b/monkey/common/cloud/aws_service.py
index 6479721c8..41bb202bc 100644
--- a/monkey/common/cloud/aws_service.py
+++ b/monkey/common/cloud/aws_service.py
@@ -1,23 +1,44 @@
+import logging
+
import boto3
+import botocore
from botocore.exceptions import ClientError
-__author__ = 'itay.mizeretz'
+from common.cloud.aws_instance import AwsInstance
+
+__author__ = ['itay.mizeretz', 'shay.nehmad']
+
+INSTANCE_INFORMATION_LIST_KEY = 'InstanceInformationList'
+INSTANCE_ID_KEY = 'InstanceId'
+COMPUTER_NAME_KEY = 'ComputerName'
+PLATFORM_TYPE_KEY = 'PlatformType'
+IP_ADDRESS_KEY = 'IPAddress'
+
+
+logger = logging.getLogger(__name__)
+
+
+def filter_instance_data_from_aws_response(response):
+ return [{
+ 'instance_id': x[INSTANCE_ID_KEY],
+ 'name': x[COMPUTER_NAME_KEY],
+ 'os': x[PLATFORM_TYPE_KEY].lower(),
+ 'ip_address': x[IP_ADDRESS_KEY]
+ } for x in response[INSTANCE_INFORMATION_LIST_KEY]]
class AwsService(object):
"""
- Supplies various AWS services
+ A wrapper class around the boto3 client and session modules, which supplies various AWS services.
+
+ This class will assume:
+ 1. That it's running on an EC2 instance
+ 2. That the instance is associated with the correct IAM role. See
+ https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#iam-role for details.
"""
- access_key_id = None
- secret_access_key = None
region = None
- @staticmethod
- def set_auth_params(access_key_id, secret_access_key):
- AwsService.access_key_id = access_key_id
- AwsService.secret_access_key = secret_access_key
-
@staticmethod
def set_region(region):
AwsService.region = region
@@ -26,15 +47,11 @@ class AwsService(object):
def get_client(client_type, region=None):
return boto3.client(
client_type,
- aws_access_key_id=AwsService.access_key_id,
- aws_secret_access_key=AwsService.secret_access_key,
region_name=region if region is not None else AwsService.region)
@staticmethod
def get_session():
- return boto3.session.Session(
- aws_access_key_id=AwsService.access_key_id,
- aws_secret_access_key=AwsService.secret_access_key)
+ return boto3.session.Session()
@staticmethod
def get_regions():
@@ -50,14 +67,22 @@ class AwsService(object):
@staticmethod
def get_instances():
- return \
- [
- {
- 'instance_id': x['InstanceId'],
- 'name': x['ComputerName'],
- 'os': x['PlatformType'].lower(),
- 'ip_address': x['IPAddress']
- }
- for x in AwsService.get_client('ssm').describe_instance_information()['InstanceInformationList']
- ]
+ """
+ Get the information for all instances with the relevant roles.
+ This function will assume that it's running on an EC2 instance with the correct IAM role.
+ See https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#iam-role for details.
+
+ :raises: botocore.exceptions.ClientError if can't describe local instance information.
+ :return: All visible instances from this instance
+ """
+ current_instance = AwsInstance()
+ local_ssm_client = boto3.client("ssm", current_instance.get_region())
+ try:
+ response = local_ssm_client.describe_instance_information()
+
+ filtered_instances_data = filter_instance_data_from_aws_response(response)
+ return filtered_instances_data
+ except botocore.exceptions.ClientError as e:
+ logger.warning("AWS client error while trying to get instances: " + e.message)
+ raise e
diff --git a/monkey/common/cloud/test_filter_instance_data_from_aws_response.py b/monkey/common/cloud/test_filter_instance_data_from_aws_response.py
new file mode 100644
index 000000000..8aec518d3
--- /dev/null
+++ b/monkey/common/cloud/test_filter_instance_data_from_aws_response.py
@@ -0,0 +1,59 @@
+from unittest import TestCase
+from aws_service import filter_instance_data_from_aws_response
+
+import json
+
+
+__author__ = 'shay.nehmad'
+
+
+class TestFilter_instance_data_from_aws_response(TestCase):
+ def test_filter_instance_data_from_aws_response(self):
+ json_response_full = """
+ {
+ "InstanceInformationList": [
+ {
+ "ActivationId": "string",
+ "AgentVersion": "string",
+ "AssociationOverview": {
+ "DetailedStatus": "string",
+ "InstanceAssociationStatusAggregatedCount": {
+ "string" : 6
+ }
+ },
+ "AssociationStatus": "string",
+ "ComputerName": "string",
+ "IamRole": "string",
+ "InstanceId": "string",
+ "IPAddress": "string",
+ "IsLatestVersion": "True",
+ "LastAssociationExecutionDate": 6,
+ "LastPingDateTime": 6,
+ "LastSuccessfulAssociationExecutionDate": 6,
+ "Name": "string",
+ "PingStatus": "string",
+ "PlatformName": "string",
+ "PlatformType": "string",
+ "PlatformVersion": "string",
+ "RegistrationDate": 6,
+ "ResourceType": "string"
+ }
+ ],
+ "NextToken": "string"
+ }
+ """
+
+ json_response_empty = """
+ {
+ "InstanceInformationList": [],
+ "NextToken": "string"
+ }
+ """
+
+ self.assertEqual(filter_instance_data_from_aws_response(json.loads(json_response_empty)), [])
+ self.assertEqual(
+ filter_instance_data_from_aws_response(json.loads(json_response_full)),
+ [{'instance_id': u'string',
+ 'ip_address': u'string',
+ 'name': u'string',
+ 'os': u'string'}])
diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py
index 4f055fec6..b1697f5a5 100644
--- a/monkey/monkey_island/cc/app.py
+++ b/monkey/monkey_island/cc/app.py
@@ -30,6 +30,7 @@ from monkey_island.cc.resources.telemetry_feed import TelemetryFeed
from monkey_island.cc.resources.pba_file_download import PBAFileDownload
from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
+from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
from monkey_island.cc.resources.pba_file_upload import FileUpload
from monkey_island.cc.resources.attack_telem import AttackTelem
@@ -101,6 +102,9 @@ def init_app(mongo_url):
database.init()
ConfigService.init_config()
+ # If running on AWS, this will initialize the instance data, which is used "later" in the execution of the island.
+ RemoteRunAwsService.init()
+
app.add_url_rule('/', 'serve_home', serve_home)
app.add_url_rule('/', 'serve_static_file', serve_static_file)
diff --git a/monkey/monkey_island/cc/exporter_init.py b/monkey/monkey_island/cc/exporter_init.py
index 3450e98f2..fdf26fe8f 100644
--- a/monkey/monkey_island/cc/exporter_init.py
+++ b/monkey/monkey_island/cc/exporter_init.py
@@ -1,19 +1,19 @@
-from monkey_island.cc.environment.environment import load_env_from_file, AWS
+import logging
+
from monkey_island.cc.report_exporter_manager import ReportExporterManager
from monkey_island.cc.resources.aws_exporter import AWSExporter
+from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
-__author__ = 'maor.rayzin'
+logger = logging.getLogger(__name__)
def populate_exporter_list():
-
manager = ReportExporterManager()
- if is_aws_exporter_required():
+ RemoteRunAwsService.init()
+ if RemoteRunAwsService.is_running_on_aws():
manager.add_exporter_to_list(AWSExporter)
+ if len(manager.get_exporters_list()) != 0:
+ logger.debug(
+ "Populated exporters list with the following exporters: {0}".format(str(manager.get_exporters_list())))
-def is_aws_exporter_required():
- if str(load_env_from_file()) == AWS:
- return True
- else:
- return False
diff --git a/monkey/monkey_island/cc/report_exporter_manager.py b/monkey/monkey_island/cc/report_exporter_manager.py
index a6a983a20..5e51a43e1 100644
--- a/monkey/monkey_island/cc/report_exporter_manager.py
+++ b/monkey/monkey_island/cc/report_exporter_manager.py
@@ -29,6 +29,7 @@ class ReportExporterManager(object):
def export(self, report):
try:
for exporter in self._exporters_set:
+ logger.debug("Trying to export using " + repr(exporter))
exporter().handle_report(report)
except Exception as e:
- logger.exception('Failed to export report')
+ logger.exception('Failed to export report, error: ' + e.message)
diff --git a/monkey/monkey_island/cc/resources/aws_exporter.py b/monkey/monkey_island/cc/resources/aws_exporter.py
index b08c16ae6..7e1f17c48 100644
--- a/monkey/monkey_island/cc/resources/aws_exporter.py
+++ b/monkey/monkey_island/cc/resources/aws_exporter.py
@@ -1,53 +1,42 @@
import logging
import uuid
from datetime import datetime
+
import boto3
from botocore.exceptions import UnknownServiceError
-from monkey_island.cc.resources.exporter import Exporter
-from monkey_island.cc.services.config import ConfigService
-from monkey_island.cc.environment.environment import load_server_configuration_from_file
from common.cloud.aws_instance import AwsInstance
+from monkey_island.cc.environment.environment import load_server_configuration_from_file
+from monkey_island.cc.resources.exporter import Exporter
-__author__ = 'maor.rayzin'
-
+__authors__ = ['maor.rayzin', 'shay.nehmad']
logger = logging.getLogger(__name__)
-AWS_CRED_CONFIG_KEYS = [['cnc', 'aws_config', 'aws_access_key_id'],
- ['cnc', 'aws_config', 'aws_secret_access_key'],
- ['cnc', 'aws_config', 'aws_account_id']]
-
class AWSExporter(Exporter):
-
@staticmethod
def handle_report(report_json):
- aws = AwsInstance()
+
findings_list = []
issues_list = report_json['recommendations']['issues']
if not issues_list:
logger.info('No issues were found by the monkey, no need to send anything')
return True
+
+ current_aws_region = AwsInstance().get_region()
+
for machine in issues_list:
for issue in issues_list[machine]:
if issue.get('aws_instance_id', None):
- findings_list.append(AWSExporter._prepare_finding(issue, aws.get_region()))
+ findings_list.append(AWSExporter._prepare_finding(issue, current_aws_region))
- if not AWSExporter._send_findings(findings_list, AWSExporter._get_aws_keys(), aws.get_region()):
+ if not AWSExporter._send_findings(findings_list, current_aws_region):
logger.error('Exporting findings to aws failed')
return False
return True
- @staticmethod
- def _get_aws_keys():
- creds_dict = {}
- for key in AWS_CRED_CONFIG_KEYS:
- creds_dict[key[2]] = str(ConfigService.get_config_value(key))
-
- return creds_dict
-
@staticmethod
def merge_two_dicts(x, y):
z = x.copy() # start with x's keys and values
@@ -82,7 +71,8 @@ class AWSExporter(Exporter):
configured_product_arn = load_server_configuration_from_file()['aws'].get('sec_hub_product_arn', '')
product_arn = 'arn:aws:securityhub:{region}:{arn}'.format(region=region, arn=configured_product_arn)
instance_arn = 'arn:aws:ec2:' + str(region) + ':instance:{instance_id}'
- account_id = AWSExporter._get_aws_keys().get('aws_account_id', '')
+ account_id = AwsInstance().get_account_id()
+ logger.debug("aws account id acquired: {}".format(account_id))
finding = {
"SchemaVersion": "2018-10-08",
@@ -100,27 +90,26 @@ class AWSExporter(Exporter):
return AWSExporter.merge_two_dicts(finding, findings_dict[issue['type']](issue, instance_arn))
@staticmethod
- def _send_findings(findings_list, creds_dict, region):
+ def _send_findings(findings_list, region):
try:
- if not creds_dict:
- logger.info('No AWS access credentials received in configuration')
- return False
+ logger.debug("Trying to acquire securityhub boto3 client in " + region)
+ security_hub_client = boto3.client('securityhub', region_name=region)
+ logger.debug("Client acquired: {0}".format(repr(security_hub_client)))
- securityhub = boto3.client('securityhub',
- aws_access_key_id=creds_dict.get('aws_access_key_id', ''),
- aws_secret_access_key=creds_dict.get('aws_secret_access_key', ''),
- region_name=region)
+ # Assumes the machine has the correct IAM role to do this, @see
+ # https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances
+ import_response = security_hub_client.batch_import_findings(Findings=findings_list)
+ logger.debug("Import findings response: {0}".format(repr(import_response)))
- import_response = securityhub.batch_import_findings(Findings=findings_list)
if import_response['ResponseMetadata']['HTTPStatusCode'] == 200:
return True
else:
return False
except UnknownServiceError as e:
- logger.warning('AWS exporter called but AWS-CLI securityhub service is not installed')
+ logger.warning('AWS exporter called but AWS-CLI securityhub service is not installed. Error: ' + e.message)
return False
except Exception as e:
- logger.exception('AWS security hub findings failed to send.')
+ logger.exception('AWS security hub findings failed to send. Error: ' + e.message)
return False
@staticmethod
@@ -159,7 +148,7 @@ class AWSExporter(Exporter):
title="Weak segmentation - Machines were able to communicate over unused ports.",
description="Use micro-segmentation policies to disable communication other than the required.",
recommendation="Machines are not locked down at port level. Network tunnel was set up from {0} to {1}"
- .format(issue['machine'], issue['dest']),
+ .format(issue['machine'], issue['dest']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -171,9 +160,9 @@ class AWSExporter(Exporter):
severity=10,
title="Samba servers are vulnerable to 'SambaCry'",
description="Change {0} password to a complex one-use password that is not shared with other computers on the network. Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up." \
- .format(issue['username']),
+ .format(issue['username']),
recommendation="The machine {0} ({1}) is vulnerable to a SambaCry attack. The Monkey authenticated over the SMB protocol with user {2} and its password, and used the SambaCry vulnerability.".format(
- issue['machine'], issue['ip_address'], issue['username']),
+ issue['machine'], issue['ip_address'], issue['username']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -185,9 +174,9 @@ class AWSExporter(Exporter):
severity=5,
title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.",
description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format(
- issue['username']),
+ issue['username']),
recommendation="The machine {0}({1}) is vulnerable to a SMB attack. The Monkey used a pass-the-hash attack over SMB protocol with user {2}.".format(
- issue['machine'], issue['ip_address'], issue['username']),
+ issue['machine'], issue['ip_address'], issue['username']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -199,9 +188,9 @@ class AWSExporter(Exporter):
severity=1,
title="Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.",
description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format(
- issue['username']),
+ issue['username']),
recommendation="The machine {0} ({1}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH protocol with user {2} and its password.".format(
- issue['machine'], issue['ip_address'], issue['username']),
+ issue['machine'], issue['ip_address'], issue['username']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -214,7 +203,7 @@ class AWSExporter(Exporter):
title="Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.",
description="Protect {ssh_key} private key with a pass phrase.".format(ssh_key=issue['ssh_key']),
recommendation="The machine {machine} ({ip_address}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH protocol with private key {ssh_key}.".format(
- machine=issue['machine'], ip_address=issue['ip_address'], ssh_key=issue['ssh_key']),
+ machine=issue['machine'], ip_address=issue['ip_address'], ssh_key=issue['ssh_key']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -227,7 +216,7 @@ class AWSExporter(Exporter):
title="Elastic Search servers are vulnerable to CVE-2015-1427",
description="Update your Elastic Search server to version 1.4.3 and up.",
recommendation="The machine {0}({1}) is vulnerable to an Elastic Groovy attack. The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427.".format(
- issue['machine'], issue['ip_address']),
+ issue['machine'], issue['ip_address']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -243,7 +232,8 @@ class AWSExporter(Exporter):
{0} in the networks {1} \
could directly access the Monkey Island server in the networks {2}.".format(issue['machine'],
issue['networks'],
- issue['server_networks']),
+ issue[
+ 'server_networks']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -269,7 +259,7 @@ class AWSExporter(Exporter):
description="Update your Bash to a ShellShock-patched version.",
recommendation="The machine {0} ({1}) is vulnerable to a ShellShock attack. "
"The attack was made possible because the HTTP server running on TCP port {2} was vulnerable to a shell injection attack on the paths: {3}.".format(
- issue['machine'], issue['ip_address'], issue['port'], issue['paths']),
+ issue['machine'], issue['ip_address'], issue['port'], issue['paths']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -281,9 +271,9 @@ class AWSExporter(Exporter):
severity=1,
title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.",
description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format(
- issue['username']),
+ issue['username']),
recommendation="The machine {0} ({1}) is vulnerable to a SMB attack. The Monkey authenticated over the SMB protocol with user {2} and its password.".format(
- issue['machine'], issue['ip_address'], issue['username']),
+ issue['machine'], issue['ip_address'], issue['username']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -296,7 +286,7 @@ class AWSExporter(Exporter):
title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.",
description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.",
recommendation="The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey authenticated over the WMI protocol with user {username} and its password.".format(
- machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']),
+ machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -308,9 +298,9 @@ class AWSExporter(Exporter):
severity=1,
title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.",
description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format(
- issue['username']),
+ issue['username']),
recommendation="The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey used a pass-the-hash attack over WMI protocol with user {username}".format(
- machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']),
+ machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -322,9 +312,9 @@ class AWSExporter(Exporter):
severity=1,
title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.",
description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format(
- issue['username']),
+ issue['username']),
recommendation="The machine machine ({ip_address}) is vulnerable to a RDP attack. The Monkey authenticated over the RDP protocol with user {username} and its password.".format(
- machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']),
+ machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -337,7 +327,7 @@ class AWSExporter(Exporter):
title="Multiple users have the same password.",
description="Some domain users are sharing passwords, this should be fixed by changing passwords.",
recommendation="These users are sharing access password: {shared_with}.".format(
- shared_with=issue['shared_with']),
+ shared_with=issue['shared_with']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -350,7 +340,7 @@ class AWSExporter(Exporter):
title="Shared local administrator account - Different machines have the same account as a local administrator.",
description="Make sure the right administrator accounts are managing the right machines, and that there isn\'t an unintentional local admin sharing.",
recommendation="Here is a list of machines which the account {username} is defined as an administrator: {shared_machines}".format(
- username=issue['username'], shared_machines=issue['shared_machines']),
+ username=issue['username'], shared_machines=issue['shared_machines']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -363,7 +353,7 @@ class AWSExporter(Exporter):
title="Mimikatz found login credentials of a user who has admin access to a server defined as critical.",
description="This critical machine is open to attacks via strong users with access to it.",
recommendation="The services: {services} have been found on the machine thus classifying it as a critical machine. These users has access to it:{threatening_users}.".format(
- services=issue['services'], threatening_users=issue['threatening_users']),
+ services=issue['services'], threatening_users=issue['threatening_users']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -377,7 +367,7 @@ class AWSExporter(Exporter):
description="Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions.",
recommendation="Struts2 server at {machine} ({ip_address}) is vulnerable to remote code execution attack."
" The attack was made possible because the server is using an old version of Jakarta based file upload Multipart parser.".format(
- machine=issue['machine'], ip_address=issue['ip_address']),
+ machine=issue['machine'], ip_address=issue['ip_address']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
@@ -392,7 +382,7 @@ class AWSExporter(Exporter):
"Vulnerable versions are 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0.",
recommendation="Oracle WebLogic server at {machine} ({ip_address}) is vulnerable to remote code execution attack."
" The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware (subcomponent: WLS Security).".format(
- machine=issue['machine'], ip_address=issue['ip_address']),
+ machine=issue['machine'], ip_address=issue['ip_address']),
instance_arn=instance_arn,
instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None
)
diff --git a/monkey/monkey_island/cc/resources/remote_run.py b/monkey/monkey_island/cc/resources/remote_run.py
index ccd9fbbbe..675c292c1 100644
--- a/monkey/monkey_island/cc/resources/remote_run.py
+++ b/monkey/monkey_island/cc/resources/remote_run.py
@@ -1,4 +1,6 @@
import json
+
+from botocore.exceptions import NoCredentialsError, ClientError
from flask import request, jsonify, make_response
import flask_restful
@@ -6,6 +8,11 @@ from monkey_island.cc.auth import jwt_required
from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
from common.cloud.aws_service import AwsService
+CLIENT_ERROR_FORMAT = "ClientError, error message: '{}'. Probably, the IAM role that has been associated with the " \
+ "instance doesn't permit SSM calls. "
+NO_CREDS_ERROR_FORMAT = "NoCredentialsError, error message: '{}'. Probably, no IAM role has been associated with the " \
+ "instance. "
+
class RemoteRun(flask_restful.Resource):
def __init__(self):
@@ -24,10 +31,14 @@ class RemoteRun(flask_restful.Resource):
is_aws = RemoteRunAwsService.is_running_on_aws()
resp = {'is_aws': is_aws}
if is_aws:
- is_auth = RemoteRunAwsService.update_aws_auth_params()
- resp['auth'] = is_auth
- if is_auth:
+ try:
resp['instances'] = AwsService.get_instances()
+ except NoCredentialsError as e:
+ resp['error'] = NO_CREDS_ERROR_FORMAT.format(e.message)
+ return jsonify(resp)
+ except ClientError as e:
+ resp['error'] = CLIENT_ERROR_FORMAT.format(e.message)
+ return jsonify(resp)
return jsonify(resp)
return {}
@@ -37,11 +48,9 @@ class RemoteRun(flask_restful.Resource):
body = json.loads(request.data)
resp = {}
if body.get('type') == 'aws':
- is_auth = RemoteRunAwsService.update_aws_auth_params()
- resp['auth'] = is_auth
- if is_auth:
- result = self.run_aws_monkeys(body)
- resp['result'] = result
+ RemoteRunAwsService.update_aws_region_authless()
+ result = self.run_aws_monkeys(body)
+ resp['result'] = result
return jsonify(resp)
# default action
diff --git a/monkey/monkey_island/cc/resources/telemetry_feed.py b/monkey/monkey_island/cc/resources/telemetry_feed.py
index f01ff90d6..01fdcc51c 100644
--- a/monkey/monkey_island/cc/resources/telemetry_feed.py
+++ b/monkey/monkey_island/cc/resources/telemetry_feed.py
@@ -54,7 +54,7 @@ class TelemetryFeed(flask_restful.Resource):
@staticmethod
def get_state_telem_brief(telem):
if telem['data']['done']:
- return '''Monkey finishing it's execution.'''
+ return '''Monkey finishing its execution.'''
else:
return 'Monkey started.'
diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py
index 171fbb7bf..c52ceb3f4 100644
--- a/monkey/monkey_island/cc/services/config.py
+++ b/monkey/monkey_island/cc/services/config.py
@@ -26,14 +26,6 @@ ENCRYPTED_CONFIG_ARRAYS = \
['internal', 'exploits', 'exploit_ssh_keys']
]
-# This should be used for config values of string type
-ENCRYPTED_CONFIG_STRINGS = \
- [
- ['cnc', 'aws_config', 'aws_access_key_id'],
- ['cnc', 'aws_config', 'aws_account_id'],
- ['cnc', 'aws_config', 'aws_secret_access_key']
- ]
-
class ConfigService:
default_config = None
@@ -76,8 +68,6 @@ class ConfigService:
if should_decrypt:
if config_key_as_arr in ENCRYPTED_CONFIG_ARRAYS:
config = [encryptor.dec(x) for x in config]
- elif config_key_as_arr in ENCRYPTED_CONFIG_STRINGS:
- config = encryptor.dec(config)
return config
@staticmethod
@@ -243,11 +233,8 @@ class ConfigService:
"""
Same as decrypt_config but for a flat configuration
"""
- if is_island:
- keys = [config_arr_as_array[2] for config_arr_as_array in
- (ENCRYPTED_CONFIG_ARRAYS + ENCRYPTED_CONFIG_STRINGS)]
- else:
- keys = [config_arr_as_array[2] for config_arr_as_array in ENCRYPTED_CONFIG_ARRAYS]
+ keys = [config_arr_as_array[2] for config_arr_as_array in ENCRYPTED_CONFIG_ARRAYS]
+
for key in keys:
if isinstance(flat_config[key], collections.Sequence) and not isinstance(flat_config[key], string_types):
# Check if we are decrypting ssh key pair
@@ -261,7 +248,7 @@ class ConfigService:
@staticmethod
def _encrypt_or_decrypt_config(config, is_decrypt=False):
- for config_arr_as_array in (ENCRYPTED_CONFIG_ARRAYS + ENCRYPTED_CONFIG_STRINGS):
+ for config_arr_as_array in ENCRYPTED_CONFIG_ARRAYS:
config_arr = config
parent_config_arr = None
diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py
index 382b591db..3ec3c7212 100644
--- a/monkey/monkey_island/cc/services/config_schema.py
+++ b/monkey/monkey_island/cc/services/config_schema.py
@@ -695,31 +695,6 @@ SCHEMA = {
}
}
},
- 'aws_config': {
- 'title': 'AWS Configuration',
- 'type': 'object',
- 'description': 'These credentials will be used in order to export the monkey\'s findings to the AWS Security Hub.',
- 'properties': {
- 'aws_account_id': {
- 'title': 'AWS account ID',
- 'type': 'string',
- 'description': 'Your AWS account ID that is subscribed to security hub feeds',
- 'default': ''
- },
- 'aws_access_key_id': {
- 'title': 'AWS access key ID',
- 'type': 'string',
- 'description': 'Your AWS public access key ID, can be found in the IAM user interface in the AWS console.',
- 'default': ''
- },
- 'aws_secret_access_key': {
- 'title': 'AWS secret access key',
- 'type': 'string',
- 'description': 'Your AWS secret access key id, you can get this after creating a public access key in the console.',
- 'default': ''
- }
- }
- }
}
},
"exploits": {
diff --git a/monkey/monkey_island/cc/services/remote_run_aws.py b/monkey/monkey_island/cc/services/remote_run_aws.py
index 7cc26008d..78df00721 100644
--- a/monkey/monkey_island/cc/services/remote_run_aws.py
+++ b/monkey/monkey_island/cc/services/remote_run_aws.py
@@ -46,22 +46,12 @@ class RemoteRunAwsService:
return RemoteRunAwsService.aws_instance.is_aws_instance()
@staticmethod
- def update_aws_auth_params():
+ def update_aws_region_authless():
"""
- Updates the AWS authentication parameters according to config
- :return: True if new params allow successful authentication. False otherwise
+ Updates the AWS region without auth params (via IAM role)
"""
- access_key_id = ConfigService.get_config_value(['cnc', 'aws_config', 'aws_access_key_id'], False, True)
- secret_access_key = ConfigService.get_config_value(['cnc', 'aws_config', 'aws_secret_access_key'], False, True)
-
- if (access_key_id != AwsService.access_key_id) or (secret_access_key != AwsService.secret_access_key):
- AwsService.set_auth_params(access_key_id, secret_access_key)
- RemoteRunAwsService.is_auth = AwsService.test_client()
-
AwsService.set_region(RemoteRunAwsService.aws_instance.region)
- return RemoteRunAwsService.is_auth
-
@staticmethod
def get_bitness(instances):
"""
diff --git a/monkey/monkey_island/cc/ui/.babelrc b/monkey/monkey_island/cc/ui/.babelrc
index 9e8720a99..31130e826 100644
--- a/monkey/monkey_island/cc/ui/.babelrc
+++ b/monkey/monkey_island/cc/ui/.babelrc
@@ -1,4 +1,4 @@
{
- "presets": ["es2015", "stage-0", "react"]
-
+ "presets": ["es2015", "stage-0", "react"],
+ "plugins": ["emotion"]
}
diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json
index 81322b43f..58208ef24 100644
--- a/monkey/monkey_island/cc/ui/package-lock.json
+++ b/monkey/monkey_island/cc/ui/package-lock.json
@@ -53,6 +53,36 @@
"@babel/types": "7.0.0-beta.44"
}
},
+ "@babel/helper-module-imports": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
+ "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
+ "requires": {
+ "@babel/types": "^7.0.0"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz",
+ "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==",
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.11",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
+ }
+ }
+ },
"@babel/helper-split-export-declaration": {
"version": "7.0.0-beta.44",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz",
@@ -221,6 +251,86 @@
}
}
},
+ "@emotion/cache": {
+ "version": "10.0.9",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.9.tgz",
+ "integrity": "sha512-f7MblpE2xoimC4fCMZ9pivmsIn7hyWRIvY75owMDi8pdOSeh+w5tH3r4hBJv/LLrwiMM7cTQURqTPcYoL5pWnw==",
+ "requires": {
+ "@emotion/sheet": "0.9.2",
+ "@emotion/stylis": "0.8.3",
+ "@emotion/utils": "0.11.1",
+ "@emotion/weak-memoize": "0.2.2"
+ }
+ },
+ "@emotion/core": {
+ "version": "10.0.10",
+ "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.10.tgz",
+ "integrity": "sha512-U1aE2cOWUscjc8ZJ3Cx32udOzLeRoJwGxBH93xQD850oQFpwPKZARzdUtdc9SByUOwzSFYxhDhrpXnV34FJmWg==",
+ "requires": {
+ "@emotion/cache": "^10.0.9",
+ "@emotion/css": "^10.0.9",
+ "@emotion/serialize": "^0.11.6",
+ "@emotion/sheet": "0.9.2",
+ "@emotion/utils": "0.11.1"
+ }
+ },
+ "@emotion/css": {
+ "version": "10.0.9",
+ "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.9.tgz",
+ "integrity": "sha512-jtHhUSWw+L7yxYgNtC+KJ3Ory90/jiAtpG1qT+gTQQ/RR5AMiigs9/lDHu/vnwljaq2S48FoKb/FZZMlJcC4bw==",
+ "requires": {
+ "@emotion/serialize": "^0.11.6",
+ "@emotion/utils": "0.11.1",
+ "babel-plugin-emotion": "^10.0.9"
+ }
+ },
+ "@emotion/hash": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.1.tgz",
+ "integrity": "sha512-OYpa/Sg+2GDX+jibUfpZVn1YqSVRpYmTLF2eyAfrFTIJSbwyIrc+YscayoykvaOME/wV4BV0Sa0yqdMrgse6mA=="
+ },
+ "@emotion/memoize": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz",
+ "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg=="
+ },
+ "@emotion/serialize": {
+ "version": "0.11.6",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.6.tgz",
+ "integrity": "sha512-n4zVv2qGLmspF99jaEUwnMV0fnEGsyUMsC/8KZKUSUTZMYljHE+j+B6rSD8PIFtaUIhHaxCG2JawN6L+OgLN0Q==",
+ "requires": {
+ "@emotion/hash": "0.7.1",
+ "@emotion/memoize": "0.7.1",
+ "@emotion/unitless": "0.7.3",
+ "@emotion/utils": "0.11.1",
+ "csstype": "^2.5.7"
+ }
+ },
+ "@emotion/sheet": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.2.tgz",
+ "integrity": "sha512-pVBLzIbC/QCHDKJF2E82V2H/W/B004mDFQZiyo/MSR+VC4pV5JLG0TF/zgQDFvP3fZL/5RTPGEmXlYJBMUuJ+A=="
+ },
+ "@emotion/stylis": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.3.tgz",
+ "integrity": "sha512-M3nMfJ6ndJMYloSIbYEBq6G3eqoYD41BpDOxreE8j0cb4fzz/5qvmqU9Mb2hzsXcCnIlGlWhS03PCzVGvTAe0Q=="
+ },
+ "@emotion/unitless": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz",
+ "integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg=="
+ },
+ "@emotion/utils": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.1.tgz",
+ "integrity": "sha512-8M3VN0hetwhsJ8dH8VkVy7xo5/1VoBsDOk/T4SJOeXwTO1c4uIqVNx2qyecLFnnUWD5vvUqHQ1gASSeUN6zcTg=="
+ },
+ "@emotion/weak-memoize": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz",
+ "integrity": "sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA=="
+ },
"@webassemblyjs/ast": {
"version": "1.7.8",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.8.tgz",
@@ -575,7 +685,6 @@
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
"integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
- "dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
@@ -663,6 +772,11 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ },
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
@@ -1266,6 +1380,55 @@
"babel-runtime": "^6.22.0"
}
},
+ "babel-plugin-emotion": {
+ "version": "10.0.9",
+ "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.9.tgz",
+ "integrity": "sha512-IfWP12e9/wHtWHxVTzD692Nbcmrmcz2tip7acp6YUqtrP7slAyr5B+69hyZ8jd55GsyNSZwryNnmuDEVe0j+7w==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@emotion/hash": "0.7.1",
+ "@emotion/memoize": "0.7.1",
+ "@emotion/serialize": "^0.11.6",
+ "babel-plugin-macros": "^2.0.0",
+ "babel-plugin-syntax-jsx": "^6.18.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^1.0.5",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
+ }
+ },
+ "babel-plugin-macros": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.5.1.tgz",
+ "integrity": "sha512-xN3KhAxPzsJ6OQTktCanNpIFnnMsCV+t8OloKxIL72D6+SUZYFn9qfklPgef5HyyDtzYZqqb+fs1S12+gQY82Q==",
+ "requires": {
+ "@babel/runtime": "^7.4.2",
+ "cosmiconfig": "^5.2.0",
+ "resolve": "^1.10.0"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz",
+ "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==",
+ "requires": {
+ "regenerator-runtime": "^0.13.2"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.2",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
+ "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
+ }
+ }
+ },
"babel-plugin-syntax-async-functions": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
@@ -1335,8 +1498,7 @@
"babel-plugin-syntax-jsx": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
- "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
- "dev": true
+ "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
},
"babel-plugin-syntax-object-rest-spread": {
"version": "6.13.0",
@@ -2525,6 +2687,21 @@
}
}
},
+ "caller-callsite": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+ "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+ "requires": {
+ "callsites": "^2.0.0"
+ },
+ "dependencies": {
+ "callsites": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
+ }
+ }
+ },
"caller-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
@@ -2631,6 +2808,11 @@
"supports-color": "^2.0.0"
}
},
+ "change-emitter": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
+ "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
+ },
"chardet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
@@ -3018,8 +3200,7 @@
"convert-source-map": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
- "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=",
- "dev": true
+ "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU="
},
"cookie": {
"version": "0.3.1",
@@ -3056,7 +3237,7 @@
"copy-to-clipboard": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz",
- "integrity": "sha1-9OgvSogw3ORma3643tDJvMMTq6k=",
+ "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==",
"requires": {
"toggle-selection": "^1.0.3"
}
@@ -3143,6 +3324,42 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
+ "cosmiconfig": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz",
+ "integrity": "sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==",
+ "requires": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.0",
+ "parse-json": "^4.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ }
+ }
+ },
"create-ecdh": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
@@ -3322,6 +3539,11 @@
"integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=",
"dev": true
},
+ "csstype": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.3.tgz",
+ "integrity": "sha512-rINUZXOkcBmoHWEyu7JdHu5JMzkGRoMX4ov9830WNgxf5UYxcBUO0QTKAqeJ5EZfSdlrcJYkC8WwfVW7JYi4yg=="
+ },
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@@ -3975,7 +4197,6 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
@@ -4061,8 +4282,7 @@
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"escodegen": {
"version": "1.8.1",
@@ -4419,8 +4639,7 @@
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
- "dev": true
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
},
"etag": {
"version": "1.8.1",
@@ -4816,6 +5035,27 @@
"websocket-driver": ">=0.5.1"
}
},
+ "fbjs": {
+ "version": "0.8.17",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
+ "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+ "requires": {
+ "core-js": "^1.0.0",
+ "isomorphic-fetch": "^2.1.1",
+ "loose-envify": "^1.0.0",
+ "object-assign": "^4.1.0",
+ "promise": "^7.1.1",
+ "setimmediate": "^1.0.5",
+ "ua-parser-js": "^0.7.18"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+ }
+ }
+ },
"fd-slicer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
@@ -4945,6 +5185,11 @@
"pkg-dir": "^2.0.0"
}
},
+ "find-root": {
+ "version": "1.1.0",
+ "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",
@@ -5222,8 +5467,8 @@
"dev": true,
"optional": true,
"requires": {
- "delegates": "1.0.0",
- "readable-stream": "2.2.9"
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
}
},
"asn1": {
@@ -5268,14 +5513,13 @@
"dev": true,
"optional": true,
"requires": {
- "tweetnacl": "0.14.5"
+ "tweetnacl": "^0.14.3"
}
},
"block-stream": {
"version": "0.0.9",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"inherits": "~2.0.0"
}
@@ -5284,7 +5528,6 @@
"version": "2.10.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"hoek": "2.x.x"
}
@@ -5293,7 +5536,6 @@
"version": "1.1.7",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"balanced-match": "^0.4.1",
"concat-map": "0.0.1"
@@ -5320,14 +5562,12 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"combined-stream": {
"version": "1.0.5",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"delayed-stream": "~1.0.0"
}
@@ -5335,20 +5575,17 @@
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"core-util-is": {
"version": "1.0.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"cryptiles": {
"version": "2.0.5",
@@ -5394,8 +5631,7 @@
"delayed-stream": {
"version": "1.0.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"delegates": {
"version": "1.0.0",
@@ -5421,8 +5657,7 @@
"extsprintf": {
"version": "1.0.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"forever-agent": {
"version": "0.6.1",
@@ -5436,27 +5671,25 @@
"dev": true,
"optional": true,
"requires": {
- "asynckit": "0.4.0",
- "combined-stream": "1.0.5",
- "mime-types": "2.1.15"
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.5",
+ "mime-types": "^2.1.12"
}
},
"fs.realpath": {
"version": "1.0.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"fstream": {
"version": "1.0.11",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "graceful-fs": "4.1.11",
- "inherits": "2.0.3",
- "mkdirp": "0.5.1",
- "rimraf": "2.6.1"
+ "graceful-fs": "^4.1.2",
+ "inherits": "~2.0.0",
+ "mkdirp": ">=0.5 0",
+ "rimraf": "2"
}
},
"fstream-ignore": {
@@ -5465,9 +5698,9 @@
"dev": true,
"optional": true,
"requires": {
- "fstream": "1.0.11",
- "inherits": "2.0.3",
- "minimatch": "3.0.4"
+ "fstream": "^1.0.0",
+ "inherits": "2",
+ "minimatch": "^3.0.0"
}
},
"gauge": {
@@ -5476,14 +5709,14 @@
"dev": true,
"optional": true,
"requires": {
- "aproba": "1.1.1",
- "console-control-strings": "1.1.0",
- "has-unicode": "2.0.1",
- "object-assign": "4.1.1",
- "signal-exit": "3.0.2",
- "string-width": "1.0.2",
- "strip-ansi": "3.0.1",
- "wide-align": "1.1.2"
+ "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"
}
},
"getpass": {
@@ -5507,21 +5740,19 @@
"version": "7.1.2",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "fs.realpath": "1.0.0",
- "inflight": "1.0.6",
- "inherits": "2.0.3",
- "minimatch": "3.0.4",
- "once": "1.4.0",
- "path-is-absolute": "1.0.1"
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.1.11",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"har-schema": {
"version": "1.0.5",
@@ -5551,17 +5782,16 @@
"dev": true,
"optional": true,
"requires": {
- "boom": "2.10.1",
- "cryptiles": "2.0.5",
- "hoek": "2.16.3",
- "sntp": "1.0.9"
+ "boom": "2.x.x",
+ "cryptiles": "2.x.x",
+ "hoek": "2.x.x",
+ "sntp": "1.x.x"
}
},
"hoek": {
"version": "2.16.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"http-signature": {
"version": "1.1.1",
@@ -5569,19 +5799,18 @@
"dev": true,
"optional": true,
"requires": {
- "assert-plus": "0.2.0",
- "jsprim": "1.4.0",
- "sshpk": "1.13.0"
+ "assert-plus": "^0.2.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
}
},
"inflight": {
"version": "1.0.6",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "once": "1.4.0",
- "wrappy": "1.0.2"
+ "once": "^1.3.0",
+ "wrappy": "1"
}
},
"inherits": {
@@ -5600,9 +5829,8 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "number-is-nan": "1.0.1"
+ "number-is-nan": "^1.0.0"
}
},
"is-typedarray": {
@@ -5614,8 +5842,7 @@
"isarray": {
"version": "1.0.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"isstream": {
"version": "0.1.2",
@@ -5688,38 +5915,33 @@
"mime-db": {
"version": "1.27.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"mime-types": {
"version": "2.1.15",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "mime-db": "1.27.0"
+ "mime-db": "~1.27.0"
}
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "brace-expansion": "1.1.7"
+ "brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -5736,15 +5958,15 @@
"dev": true,
"optional": true,
"requires": {
- "mkdirp": "0.5.1",
- "nopt": "4.0.1",
- "npmlog": "4.1.0",
- "rc": "1.2.1",
- "request": "2.81.0",
- "rimraf": "2.6.1",
- "semver": "5.3.0",
- "tar": "2.2.1",
- "tar-pack": "3.4.0"
+ "mkdirp": "^0.5.1",
+ "nopt": "^4.0.1",
+ "npmlog": "^4.0.2",
+ "rc": "^1.1.7",
+ "request": "^2.81.0",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^2.2.1",
+ "tar-pack": "^3.4.0"
}
},
"nopt": {
@@ -5753,8 +5975,8 @@
"dev": true,
"optional": true,
"requires": {
- "abbrev": "1.1.0",
- "osenv": "0.1.4"
+ "abbrev": "1",
+ "osenv": "^0.1.4"
}
},
"npmlog": {
@@ -5763,17 +5985,16 @@
"dev": true,
"optional": true,
"requires": {
- "are-we-there-yet": "1.1.4",
- "console-control-strings": "1.1.0",
- "gauge": "2.7.4",
- "set-blocking": "2.0.0"
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"oauth-sign": {
"version": "0.8.2",
@@ -5793,7 +6014,7 @@
"dev": true,
"optional": true,
"requires": {
- "wrappy": "1.0.2"
+ "wrappy": "1"
}
},
"os-homedir": {
@@ -5814,15 +6035,14 @@
"dev": true,
"optional": true,
"requires": {
- "os-homedir": "1.0.2",
- "os-tmpdir": "1.0.2"
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"performance-now": {
"version": "0.2.0",
@@ -5833,8 +6053,7 @@
"process-nextick-args": {
"version": "1.0.7",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"punycode": {
"version": "1.4.1",
@@ -5854,10 +6073,10 @@
"dev": true,
"optional": true,
"requires": {
- "deep-extend": "0.4.2",
- "ini": "1.3.4",
- "minimist": "1.2.0",
- "strip-json-comments": "2.0.1"
+ "deep-extend": "~0.4.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
},
"dependencies": {
"minimist": {
@@ -5872,15 +6091,14 @@
"version": "2.2.9",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "buffer-shims": "1.0.0",
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "1.0.7",
- "string_decoder": "1.0.1",
- "util-deprecate": "1.0.2"
+ "buffer-shims": "~1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~1.0.0",
+ "util-deprecate": "~1.0.1"
}
},
"request": {
@@ -5889,44 +6107,42 @@
"dev": true,
"optional": true,
"requires": {
- "aws-sign2": "0.6.0",
- "aws4": "1.6.0",
- "caseless": "0.12.0",
- "combined-stream": "1.0.5",
- "extend": "3.0.1",
- "forever-agent": "0.6.1",
- "form-data": "2.1.4",
- "har-validator": "4.2.1",
- "hawk": "3.1.3",
- "http-signature": "1.1.1",
- "is-typedarray": "1.0.0",
- "isstream": "0.1.2",
- "json-stringify-safe": "5.0.1",
- "mime-types": "2.1.15",
- "oauth-sign": "0.8.2",
- "performance-now": "0.2.0",
- "qs": "6.4.0",
- "safe-buffer": "5.0.1",
- "stringstream": "0.0.5",
- "tough-cookie": "2.3.2",
- "tunnel-agent": "0.6.0",
- "uuid": "3.0.1"
+ "aws-sign2": "~0.6.0",
+ "aws4": "^1.2.1",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.5",
+ "extend": "~3.0.0",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.1.1",
+ "har-validator": "~4.2.1",
+ "hawk": "~3.1.3",
+ "http-signature": "~1.1.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.7",
+ "oauth-sign": "~0.8.1",
+ "performance-now": "^0.2.0",
+ "qs": "~6.4.0",
+ "safe-buffer": "^5.0.1",
+ "stringstream": "~0.0.4",
+ "tough-cookie": "~2.3.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.0.0"
}
},
"rimraf": {
"version": "2.6.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "glob": "7.1.2"
+ "glob": "^7.0.5"
}
},
"safe-buffer": {
"version": "5.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"semver": {
"version": "5.3.0",
@@ -5952,7 +6168,7 @@
"dev": true,
"optional": true,
"requires": {
- "hoek": "2.16.3"
+ "hoek": "2.x.x"
}
},
"sshpk": {
@@ -5961,15 +6177,15 @@
"dev": true,
"optional": true,
"requires": {
- "asn1": "0.2.3",
- "assert-plus": "1.0.0",
- "bcrypt-pbkdf": "1.0.1",
- "dashdash": "1.14.1",
- "ecc-jsbn": "0.1.1",
- "getpass": "0.1.7",
- "jodid25519": "1.0.2",
- "jsbn": "0.1.1",
- "tweetnacl": "0.14.5"
+ "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",
+ "jodid25519": "^1.0.0",
+ "jsbn": "~0.1.0",
+ "tweetnacl": "~0.14.0"
},
"dependencies": {
"assert-plus": {
@@ -5984,20 +6200,18 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "code-point-at": "1.1.0",
- "is-fullwidth-code-point": "1.0.0",
- "strip-ansi": "3.0.1"
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
}
},
"string_decoder": {
"version": "1.0.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "safe-buffer": "5.0.1"
+ "safe-buffer": "^5.0.1"
}
},
"stringstream": {
@@ -6010,9 +6224,8 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
}
},
"strip-json-comments": {
@@ -6025,11 +6238,10 @@
"version": "2.2.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
- "block-stream": "0.0.9",
- "fstream": "1.0.11",
- "inherits": "2.0.3"
+ "block-stream": "*",
+ "fstream": "^1.0.2",
+ "inherits": "2"
}
},
"tar-pack": {
@@ -6038,14 +6250,14 @@
"dev": true,
"optional": true,
"requires": {
- "debug": "2.6.8",
- "fstream": "1.0.11",
- "fstream-ignore": "1.0.5",
- "once": "1.4.0",
- "readable-stream": "2.2.9",
- "rimraf": "2.6.1",
- "tar": "2.2.1",
- "uid-number": "0.0.6"
+ "debug": "^2.2.0",
+ "fstream": "^1.0.10",
+ "fstream-ignore": "^1.0.5",
+ "once": "^1.3.3",
+ "readable-stream": "^2.1.4",
+ "rimraf": "^2.5.1",
+ "tar": "^2.2.1",
+ "uid-number": "^0.0.6"
}
},
"tough-cookie": {
@@ -6054,7 +6266,7 @@
"dev": true,
"optional": true,
"requires": {
- "punycode": "1.4.1"
+ "punycode": "^1.4.1"
}
},
"tunnel-agent": {
@@ -6063,7 +6275,7 @@
"dev": true,
"optional": true,
"requires": {
- "safe-buffer": "5.0.1"
+ "safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
@@ -6081,8 +6293,7 @@
"util-deprecate": {
"version": "1.0.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"uuid": {
"version": "3.0.1",
@@ -6105,7 +6316,7 @@
"dev": true,
"optional": true,
"requires": {
- "string-width": "1.0.2"
+ "string-width": "^1.0.2"
}
},
"wrappy": {
@@ -7076,6 +7287,30 @@
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
+ "import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+ "requires": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ },
+ "dependencies": {
+ "caller-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+ "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+ "requires": {
+ "caller-callsite": "^2.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
+ }
+ }
+ },
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@@ -7316,8 +7551,7 @@
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
- "dev": true
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
"is-binary-path": {
"version": "1.0.1",
@@ -7383,6 +7617,11 @@
}
}
},
+ "is-directory": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
+ },
"is-dotfile": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
@@ -7527,8 +7766,7 @@
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
- "dev": true
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"is-symbol": {
"version": "1.0.1",
@@ -7599,6 +7837,15 @@
}
}
},
+ "isomorphic-fetch": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+ "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+ "requires": {
+ "node-fetch": "^1.0.1",
+ "whatwg-fetch": ">=0.10.0"
+ }
+ },
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -7698,8 +7945,7 @@
"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",
- "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
- "dev": true
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
"json-schema": {
"version": "0.2.3",
@@ -8080,7 +8326,7 @@
"dev": true,
"optional": true,
"requires": {
- "balanced-match": "1.0.0",
+ "balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
@@ -8253,7 +8499,7 @@
"dev": true,
"optional": true,
"requires": {
- "brace-expansion": "1.1.11"
+ "brace-expansion": "^1.1.7"
}
},
"minimist": {
@@ -9680,6 +9926,15 @@
"lower-case": "^1.1.1"
}
},
+ "node-fetch": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+ "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "requires": {
+ "encoding": "^0.1.11",
+ "is-stream": "^1.0.1"
+ }
+ },
"node-forge": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
@@ -13007,6 +13262,11 @@
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
"dev": true
},
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+ },
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
@@ -13282,6 +13542,14 @@
"integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
"dev": true
},
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "requires": {
+ "asap": "~2.0.3"
+ }
+ },
"promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -13856,6 +14124,16 @@
}
}
},
+ "react-spinners": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.5.4.tgz",
+ "integrity": "sha512-jo7BE8prvnZNL7xNrQL16geVXH6LmaWrhvvXnmUwz2MhFO14bhlAdCLuKCwqmUJ37kGphH0C2CJRThwkjfpVzw==",
+ "requires": {
+ "@emotion/core": "^10.0.4",
+ "prop-types": "^15.5.10",
+ "recompose": "0.27.1 - 0.30.0"
+ }
+ },
"react-table": {
"version": "6.8.6",
"resolved": "https://registry.npmjs.org/react-table/-/react-table-6.8.6.tgz",
@@ -13990,6 +14268,19 @@
}
}
},
+ "recompose": {
+ "version": "0.30.0",
+ "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz",
+ "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==",
+ "requires": {
+ "@babel/runtime": "^7.0.0",
+ "change-emitter": "^0.1.2",
+ "fbjs": "^0.8.1",
+ "hoist-non-react-statics": "^2.3.1",
+ "react-lifecycles-compat": "^3.0.2",
+ "symbol-observable": "^1.0.4"
+ }
+ },
"redent": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
@@ -14246,6 +14537,14 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
+ "resolve": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+ "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
"resolve-cwd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
@@ -14272,7 +14571,7 @@
"resolve-pathname": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
- "integrity": "sha1-fpriHtgV/WOrGJre7mTcgx7vqHk="
+ "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg=="
},
"resolve-url": {
"version": "0.2.1",
@@ -14560,8 +14859,7 @@
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
- "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
- "dev": true
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"setprototypeof": {
"version": "1.1.0",
@@ -15035,8 +15333,7 @@
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
- "dev": true
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sshpk": {
"version": "1.14.1",
@@ -15682,6 +15979,11 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
+ "ua-parser-js": {
+ "version": "0.7.19",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
+ "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ=="
+ },
"uglify-js": {
"version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
@@ -16083,7 +16385,7 @@
"value-equal": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
- "integrity": "sha1-xb3S9U7gk8BIOdcc4uR1imiQq8c="
+ "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw=="
},
"vary": {
"version": "1.1.2",
@@ -18991,6 +19293,11 @@
"integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
"dev": true
},
+ "whatwg-fetch": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
+ "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
+ },
"which": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json
index 6eb6868dd..8218b89ae 100644
--- a/monkey/monkey_island/cc/ui/package.json
+++ b/monkey/monkey_island/cc/ui/package.json
@@ -93,6 +93,8 @@
"react-table": "^6.8.6",
"react-toggle": "^4.0.1",
"redux": "^4.0.0",
- "sha3": "^2.0.0"
+ "sha3": "^2.0.0",
+ "react-spinners": "^0.5.4",
+ "@emotion/core": "^10.0.10"
}
}
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
index 55e7279c0..e67b40728 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
@@ -1,11 +1,20 @@
import React from 'react';
-import {Button, Col, Well, Nav, NavItem, Collapse, Form, FormControl, FormGroup} from 'react-bootstrap';
+import { css } from '@emotion/core';
+import {Button, Col, Well, Nav, NavItem, Collapse} from 'react-bootstrap';
import CopyToClipboard from 'react-copy-to-clipboard';
+import GridLoader from 'react-spinners/GridLoader';
+
import {Icon} from 'react-fa';
import {Link} from 'react-router-dom';
import AuthComponent from '../AuthComponent';
import AwsRunTable from "../run-monkey/AwsRunTable";
+const loading_css_override = css`
+ display: block;
+ margin-right: auto;
+ margin-left: auto;
+`;
+
class RunMonkeyPageComponent extends AuthComponent {
constructor(props) {
@@ -20,12 +29,12 @@ class RunMonkeyPageComponent extends AuthComponent {
showManual: false,
showAws: false,
isOnAws: false,
- isAwsAuth: false,
awsUpdateClicked: false,
awsUpdateFailed: false,
- awsKeyId: '',
- awsSecretKey: '',
- awsMachines: []
+ awsMachines: [],
+ isLoadingAws: true,
+ isErrorWhileCollectingAwsMachines: false,
+ awsMachineCollectionErrorMsg: ''
};
}
@@ -48,13 +57,7 @@ class RunMonkeyPageComponent extends AuthComponent {
});
this.fetchAwsInfo();
- this.fetchConfig()
- .then(config => {
- this.setState({
- awsKeyId: config['cnc']['aws_config']['aws_access_key_id'],
- awsSecretKey: config['cnc']['aws_config']['aws_secret_access_key']
- });
- });
+ this.fetchConfig();
this.authFetch('/api/client-monkey')
.then(res => res.json())
@@ -75,17 +78,29 @@ class RunMonkeyPageComponent extends AuthComponent {
.then(res =>{
let is_aws = res['is_aws'];
if (is_aws) {
- this.setState({isOnAws: true, awsMachines: res['instances'], isAwsAuth: res['auth']});
+ // On AWS!
+ // Checks if there was an error while collecting the aws machines.
+ let is_error_while_collecting_aws_machines = (res['error'] != null);
+ if (is_error_while_collecting_aws_machines) {
+ // There was an error. Finish loading, and display error message.
+ this.setState({isOnAws: true, isErrorWhileCollectingAwsMachines: true, awsMachineCollectionErrorMsg: res['error'], isLoadingAws: false});
+ } else {
+ // No error! Finish loading and display machines for user
+ this.setState({isOnAws: true, awsMachines: res['instances'], isLoadingAws: false});
+ }
+ } else {
+ // Not on AWS. Finish loading and don't display the AWS div.
+ this.setState({isOnAws: false, isLoadingAws: false});
}
});
}
- generateLinuxCmd(ip, is32Bit) {
+ static generateLinuxCmd(ip, is32Bit) {
let bitText = is32Bit ? '32' : '64';
return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/monkey-linux-${bitText}; chmod +x monkey-linux-${bitText}; ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`
}
- generateWindowsCmd(ip, is32Bit) {
+ static generateWindowsCmd(ip, is32Bit) {
let bitText = is32Bit ? '32' : '64';
return `powershell [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; (New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/monkey-windows-${bitText}.exe','.\\monkey.exe'); ;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
}
@@ -118,9 +133,9 @@ class RunMonkeyPageComponent extends AuthComponent {
let is32Bit = (this.state.selectedOs.split('-')[1] === '32');
let cmdText = '';
if (isLinux) {
- cmdText = this.generateLinuxCmd(this.state.selectedIp, is32Bit);
+ cmdText = RunMonkeyPageComponent.generateLinuxCmd(this.state.selectedIp, is32Bit);
} else {
- cmdText = this.generateWindowsCmd(this.state.selectedIp, is32Bit);
+ cmdText = RunMonkeyPageComponent.generateWindowsCmd(this.state.selectedIp, is32Bit);
}
return (
@@ -148,7 +163,7 @@ class RunMonkeyPageComponent extends AuthComponent {
});
};
- renderIconByState(state) {
+ static renderIconByState(state) {
if (state === 'running') {
return
} else if (state === 'installing') {
@@ -204,19 +219,6 @@ class RunMonkeyPageComponent extends AuthComponent {
});
});
};
-
- updateAwsKeyId = (evt) => {
- this.setState({
- awsKeyId: evt.target.value
- });
- };
-
- updateAwsSecretKey = (evt) => {
- this.setState({
- awsSecretKey: evt.target.value
- });
- };
-
fetchConfig() {
return this.authFetch('/api/configuration/island')
.then(res => res.json())
@@ -224,41 +226,6 @@ class RunMonkeyPageComponent extends AuthComponent {
return res.configuration;
})
}
-
- updateAwsKeys = () => {
- this.setState({
- awsUpdateClicked: true,
- awsUpdateFailed: false
- });
- this.fetchConfig()
- .then(config => {
- let new_config = config;
- new_config['cnc']['aws_config']['aws_access_key_id'] = this.state.awsKeyId;
- new_config['cnc']['aws_config']['aws_secret_access_key'] = this.state.awsSecretKey;
- return new_config;
- })
- .then(new_config => {
- this.authFetch('/api/configuration/island',
- {
- method: 'POST',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify(new_config)
- })
- .then(res => res.json())
- .then(res => {
- this.fetchAwsInfo()
- .then(res => {
- if (!this.state.isAwsAuth) {
- this.setState({
- awsUpdateClicked: false,
- awsUpdateFailed: true
- })
- }
- });
- });
- });
- };
-
instanceIdToInstance = (instance_id) => {
let instance = this.state.awsMachines.find(
function (inst) {
@@ -268,9 +235,15 @@ class RunMonkeyPageComponent extends AuthComponent {
};
- renderAuthAwsDiv() {
+ renderAwsMachinesDiv() {
return (
+
{
this.state.ips.length > 1 ?
)
}
-
- renderNotAuthAwsDiv() {
- return (
-
-
- You haven't set your AWS account details or they're incorrect. Please enter them below to proceed.
-
-
-
-
-
-
-
- In order to remotely run commands on AWS EC2 instances, please make sure you have
- the prerequisites and if the
- instances don't show up, check the
- AWS troubleshooting guide .
-
-
- {
- this.state.awsUpdateFailed ?
-
-
Authentication failed.
-
- :
- null
- }
-
-
-
- )
- }
-
render() {
return (
@@ -364,7 +283,7 @@ class RunMonkeyPageComponent extends AuthComponent {
disabled={this.state.runningOnIslandState !== 'not_running'}
>
Run on Monkey Island Server
- { this.renderIconByState(this.state.runningOnIslandState) }
+ { RunMonkeyPageComponent.renderIconByState(this.state.runningOnIslandState) }
{
// TODO: implement button functionality
@@ -412,6 +331,21 @@ class RunMonkeyPageComponent extends AuthComponent {
{this.generateCmdDiv()}
+ {
+ this.state.isLoadingAws ?
+
+
+
+
+
+ : null
+ }
{
this.state.isOnAws ?
@@ -432,7 +366,17 @@ class RunMonkeyPageComponent extends AuthComponent {
}
{
- this.state.isAwsAuth ? this.renderAuthAwsDiv() : this.renderNotAuthAwsDiv()
+ this.state.isErrorWhileCollectingAwsMachines ?
+
+
+
+ Error while collecting AWS machine data. Error message: {this.state.awsMachineCollectionErrorMsg}
+ Are you sure you've set the correct role on your Island AWS machine?
+ Not sure what this is? Read the documentation !
+
+
+ :
+ this.renderAwsMachinesDiv()
}
diff --git a/monkey/monkey_island/requirements.txt b/monkey/monkey_island/requirements.txt
index 0203a1cea..5e0faea19 100644
--- a/monkey/monkey_island/requirements.txt
+++ b/monkey/monkey_island/requirements.txt
@@ -15,9 +15,10 @@ ipaddress
enum34
pycryptodome
boto3
+botocore
+PyInstaller
awscli
bson
cffi
-PyInstaller
virtualenv
-wheel
\ No newline at end of file
+wheel