diff --git a/deployment_scripts/config.ps1 b/deployment_scripts/config.ps1
index 24a8d3322..07be64612 100644
--- a/deployment_scripts/config.ps1
+++ b/deployment_scripts/config.ps1
@@ -22,7 +22,7 @@ $SAMBA_64_BINARY_NAME = "sc_monkey_runner64.so"
 # Other directories and paths ( most likely you dont need to configure)
 $MONKEY_ISLAND_DIR = "\monkey\monkey_island"
 $MONKEY_DIR = "\monkey\infection_monkey"
-$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\monkey_utils\sambacry_monkey_runner"
+$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\exploit\sambacry_monkey_runner"
 $PYTHON_DLL = "C:\Windows\System32\python27.dll"
 $MK32_DLL = "mk32.dll"
 $MK64_DLL = "mk64.dll"
diff --git a/deployment_scripts/deploy_linux.sh b/deployment_scripts/deploy_linux.sh
index 5ce29ac59..4df8ba114 100644
--- a/deployment_scripts/deploy_linux.sh
+++ b/deployment_scripts/deploy_linux.sh
@@ -129,7 +129,7 @@ python -m pip install --user -r requirements_linux.txt || handle_error
 # Build samba
 log_message "Building samba binaries"
 sudo apt-get install gcc-multilib
-cd ${monkey_home}/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner
+cd ${monkey_home}/monkey/infection_monkey/exploit/sambacry_monkey_runner
 sudo chmod +x ./build.sh || handle_error
 ./build.sh
 
diff --git a/monkey/common/data/__init__.py b/monkey/common/data/__init__.py
new file mode 100644
index 000000000..a8c1a93f7
--- /dev/null
+++ b/monkey/common/data/__init__.py
@@ -0,0 +1,2 @@
+from zero_trust_consts import populate_mappings
+populate_mappings()
diff --git a/monkey/common/data/network_consts.py b/monkey/common/data/network_consts.py
new file mode 100644
index 000000000..5fc9d6d8a
--- /dev/null
+++ b/monkey/common/data/network_consts.py
@@ -0,0 +1,2 @@
+ES_SERVICE = 'elastic-search-9200'
+
diff --git a/monkey/common/data/post_breach_consts.py b/monkey/common/data/post_breach_consts.py
new file mode 100644
index 000000000..dee4f67d0
--- /dev/null
+++ b/monkey/common/data/post_breach_consts.py
@@ -0,0 +1,3 @@
+POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user"
+POST_BREACH_BACKDOOR_USER = "Backdoor user"
+POST_BREACH_FILE_EXECUTION = "File execution"
diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py
new file mode 100644
index 000000000..4add05d04
--- /dev/null
+++ b/monkey/common/data/zero_trust_consts.py
@@ -0,0 +1,205 @@
+"""
+This file contains all the static data relating to Zero Trust. It is mostly used in the zero trust report generation and
+in creating findings.
+
+This file contains static mappings between zero trust components such as: pillars, principles, tests, statuses.
+Some of the mappings are computed when this module is loaded.
+"""
+
+AUTOMATION_ORCHESTRATION = u"Automation & Orchestration"
+VISIBILITY_ANALYTICS = u"Visibility & Analytics"
+WORKLOADS = u"Workloads"
+DEVICES = u"Devices"
+NETWORKS = u"Networks"
+PEOPLE = u"People"
+DATA = u"Data"
+PILLARS = (DATA, PEOPLE, NETWORKS, DEVICES, WORKLOADS, VISIBILITY_ANALYTICS, AUTOMATION_ORCHESTRATION)
+
+STATUS_UNEXECUTED = u"Unexecuted"
+STATUS_PASSED = u"Passed"
+STATUS_VERIFY = u"Verify"
+STATUS_FAILED = u"Failed"
+# Don't change order! The statuses are ordered by importance/severity.
+ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_VERIFY, STATUS_PASSED, STATUS_UNEXECUTED]
+
+TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic"
+TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http"
+TEST_MACHINE_EXPLOITED = u"machine_exploited"
+TEST_ENDPOINT_SECURITY_EXISTS = u"endpoint_security_exists"
+TEST_SCHEDULED_EXECUTION = u"scheduled_execution"
+TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline"
+TEST_SEGMENTATION = u"segmentation"
+TEST_TUNNELING = u"tunneling"
+TEST_COMMUNICATE_AS_NEW_USER = u"communicate_as_new_user"
+TESTS = (
+    TEST_SEGMENTATION,
+    TEST_MALICIOUS_ACTIVITY_TIMELINE,
+    TEST_SCHEDULED_EXECUTION,
+    TEST_ENDPOINT_SECURITY_EXISTS,
+    TEST_MACHINE_EXPLOITED,
+    TEST_DATA_ENDPOINT_HTTP,
+    TEST_DATA_ENDPOINT_ELASTIC,
+    TEST_TUNNELING,
+    TEST_COMMUNICATE_AS_NEW_USER
+)
+
+PRINCIPLE_DATA_TRANSIT = u"data_transit"
+PRINCIPLE_ENDPOINT_SECURITY = u"endpoint_security"
+PRINCIPLE_USER_BEHAVIOUR = u"user_behaviour"
+PRINCIPLE_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic"
+PRINCIPLE_SEGMENTATION = u"segmentation"
+PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
+PRINCIPLE_USERS_MAC_POLICIES = u"users_mac_policies"
+PRINCIPLES = {
+    PRINCIPLE_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
+    PRINCIPLE_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
+    PRINCIPLE_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
+    PRINCIPLE_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
+    PRINCIPLE_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
+    PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.",
+    PRINCIPLE_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory "
+                                       u"Access Control) only.",
+}
+
+POSSIBLE_STATUSES_KEY = u"possible_statuses"
+PILLARS_KEY = u"pillars"
+PRINCIPLE_KEY = u"principle_key"
+FINDING_EXPLANATION_BY_STATUS_KEY = u"finding_explanation"
+TEST_EXPLANATION_KEY = u"explanation"
+TESTS_MAP = {
+    TEST_SEGMENTATION: {
+        TEST_EXPLANATION_KEY: u"The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and logs.",
+            STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_SEGMENTATION,
+        PILLARS_KEY: [NETWORKS],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_PASSED, STATUS_FAILED]
+    },
+    TEST_MALICIOUS_ACTIVITY_TIMELINE: {
+        TEST_EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_VERIFY: "Monkey performed malicious actions in the network. Check SOC logs and alerts."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_ANALYZE_NETWORK_TRAFFIC,
+        PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY]
+    },
+    TEST_ENDPOINT_SECURITY_EXISTS: {
+        TEST_EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.",
+            STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY,
+        PILLARS_KEY: [DEVICES],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
+    },
+    TEST_MACHINE_EXPLOITED: {
+        TEST_EXPLANATION_KEY: u"The Monkey tries to exploit machines in order to breach them and propagate in the network.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.",
+            STATUS_PASSED: "Monkey didn't manage to exploit an endpoint."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY,
+        PILLARS_KEY: [DEVICES],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_VERIFY]
+    },
+    TEST_SCHEDULED_EXECUTION: {
+        TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.",
+            STATUS_PASSED: "Monkey failed to execute in a scheduled manner."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_USER_BEHAVIOUR,
+        PILLARS_KEY: [PEOPLE, NETWORKS],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY]
+    },
+    TEST_DATA_ENDPOINT_ELASTIC: {
+        TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.",
+            STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT,
+        PILLARS_KEY: [DATA],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
+    },
+    TEST_DATA_ENDPOINT_HTTP: {
+        TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to HTTP servers.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.",
+            STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT,
+        PILLARS_KEY: [DATA],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
+    },
+    TEST_TUNNELING: {
+        TEST_EXPLANATION_KEY: u"The Monkey tried to tunnel traffic using other monkeys.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES,
+        PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED]
+    },
+    TEST_COMMUNICATE_AS_NEW_USER: {
+        TEST_EXPLANATION_KEY: u"The Monkey tried to create a new user and communicate with the internet from it.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - restrict them to MAC only.",
+            STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_USERS_MAC_POLICIES,
+        PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
+    },
+}
+
+EVENT_TYPE_MONKEY_NETWORK = "monkey_network"
+EVENT_TYPE_MONKEY_LOCAL = "monkey_local"
+EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK)
+
+PILLARS_TO_TESTS = {
+    DATA: [],
+    PEOPLE: [],
+    NETWORKS: [],
+    DEVICES: [],
+    WORKLOADS: [],
+    VISIBILITY_ANALYTICS: [],
+    AUTOMATION_ORCHESTRATION: []
+}
+
+PRINCIPLES_TO_TESTS = {}
+
+PRINCIPLES_TO_PILLARS = {}
+
+
+def populate_mappings():
+    populate_pillars_to_tests()
+    populate_principles_to_tests()
+    populate_principles_to_pillars()
+
+
+def populate_pillars_to_tests():
+    for pillar in PILLARS:
+        for test, test_info in TESTS_MAP.items():
+            if pillar in test_info[PILLARS_KEY]:
+                PILLARS_TO_TESTS[pillar].append(test)
+
+
+def populate_principles_to_tests():
+    for single_principle in PRINCIPLES:
+        PRINCIPLES_TO_TESTS[single_principle] = []
+    for test, test_info in TESTS_MAP.items():
+        PRINCIPLES_TO_TESTS[test_info[PRINCIPLE_KEY]].append(test)
+
+
+def populate_principles_to_pillars():
+    for principle, principle_tests in PRINCIPLES_TO_TESTS.items():
+        principles_pillars = set()
+        for test in principle_tests:
+            for pillar in TESTS_MAP[test][PILLARS_KEY]:
+                principles_pillars.add(pillar)
+        PRINCIPLES_TO_PILLARS[principle] = principles_pillars
diff --git a/monkey/common/network/segmentation_utils.py b/monkey/common/network/segmentation_utils.py
new file mode 100644
index 000000000..9bbaabf1d
--- /dev/null
+++ b/monkey/common/network/segmentation_utils.py
@@ -0,0 +1,23 @@
+def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet):
+    """
+    Finds an IP address in ip_addresses which is in source_subnet but not in target_subnet.
+    :param ip_addresses:    List[str]: List of IP addresses to test.
+    :param source_subnet:   NetworkRange: Subnet to want an IP to not be in.
+    :param target_subnet:   NetworkRange: Subnet we want an IP to be in.
+    :return:    The cross segment IP if in source but not in target, else None. Union[str, None]
+    """
+    if get_ip_if_in_subnet(ip_addresses, target_subnet) is not None:
+        return None
+    return get_ip_if_in_subnet(ip_addresses, source_subnet)
+
+
+def get_ip_if_in_subnet(ip_addresses, subnet):
+    """
+    :param ip_addresses: IP address list.
+    :param subnet: Subnet to check if one of ip_addresses is in there. This is common.network.network_range.NetworkRange
+    :return: The first IP in ip_addresses which is in the subnet if there is one, otherwise returns None.
+    """
+    for ip_address in ip_addresses:
+        if subnet.is_in_range(ip_address):
+            return ip_address
+    return None
diff --git a/monkey/common/network/segmentation_utils_test.py b/monkey/common/network/segmentation_utils_test.py
new file mode 100644
index 000000000..56a560922
--- /dev/null
+++ b/monkey/common/network/segmentation_utils_test.py
@@ -0,0 +1,30 @@
+from common.network.network_range import *
+from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+
+
+class TestSegmentationUtils(IslandTestCase):
+    def test_get_ip_in_src_and_not_in_dst(self):
+        self.fail_if_not_testing_env()
+        source = CidrRange("1.1.1.0/24")
+        target = CidrRange("2.2.2.0/24")
+
+        # IP not in both
+        self.assertIsNone(get_ip_in_src_and_not_in_dst(
+            [text_type("3.3.3.3"), text_type("4.4.4.4")], source, target
+        ))
+
+        # IP not in source, in target
+        self.assertIsNone(get_ip_in_src_and_not_in_dst(
+            [text_type("2.2.2.2")], source, target
+        ))
+
+        # IP in source, not in target
+        self.assertIsNotNone(get_ip_in_src_and_not_in_dst(
+            [text_type("8.8.8.8"), text_type("1.1.1.1")], source, target
+        ))
+
+        # IP in both subnets
+        self.assertIsNone(get_ip_in_src_and_not_in_dst(
+            [text_type("8.8.8.8"), text_type("1.1.1.1")], source, source
+        ))
diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py
index 39fdf29b1..f1057f2dd 100644
--- a/monkey/infection_monkey/exploit/elasticgroovy.py
+++ b/monkey/infection_monkey/exploit/elasticgroovy.py
@@ -10,7 +10,8 @@ import requests
 from infection_monkey.exploit.web_rce import WebRCE
 from infection_monkey.model import WGET_HTTP_UPLOAD, BITSADMIN_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\
     DOWNLOAD_TIMEOUT
-from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE
+from infection_monkey.network.elasticfinger import ES_PORT
+from common.data.network_consts import ES_SERVICE
 from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
 from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING
 
diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index db503c717..718615114 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -10,6 +10,7 @@ from infection_monkey.exploit import HostExploiter
 from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer
 from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, build_monkey_commandline, get_monkey_depth
 from infection_monkey.model import DROPPER_ARG
+from infection_monkey.utils.monkey_dir import get_monkey_dir_path
 from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
 from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError
 
diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh b/monkey/infection_monkey/exploit/sambacry_monkey_runner/build.sh
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh
rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/build.sh
diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c b/monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.c
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c
rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.c
diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h b/monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.h
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h
rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.h
diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py
index 2ddf9127e..c20a84190 100644
--- a/monkey/infection_monkey/main.py
+++ b/monkey/infection_monkey/main.py
@@ -8,7 +8,7 @@ import os
 import sys
 import traceback
 
-import infection_monkey.utils as utils
+from infection_monkey.utils.monkey_log_path import get_dropper_log_path, get_monkey_log_path
 from infection_monkey.config import WormConfiguration, EXTERNAL_CONFIG_FILE
 from infection_monkey.dropper import MonkeyDrops
 from infection_monkey.model import MONKEY_ARG, DROPPER_ARG
@@ -79,10 +79,10 @@ def main():
 
     try:
         if MONKEY_ARG == monkey_mode:
-            log_path = utils.get_monkey_log_path()
+            log_path = get_monkey_log_path()
             monkey_cls = InfectionMonkey
         elif DROPPER_ARG == monkey_mode:
-            log_path = utils.get_dropper_log_path()
+            log_path = get_dropper_log_path()
             monkey_cls = MonkeyDrops
         else:
             return True
@@ -127,8 +127,8 @@ def main():
                 json.dump(json_dict, config_fo, skipkeys=True, sort_keys=True, indent=4, separators=(',', ': '))
 
         return True
-    except Exception:
-        LOG.exception("Exception thrown from monkey's start function")
+    except Exception as e:
+        LOG.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 cd8df4705..e8af1b60d 100644
--- a/monkey/infection_monkey/monkey.py
+++ b/monkey/infection_monkey/monkey.py
@@ -7,7 +7,8 @@ import time
 from six.moves import xrange
 
 import infection_monkey.tunnel as tunnel
-import infection_monkey.utils as utils
+from infection_monkey.utils.monkey_dir import create_monkey_dir, get_monkey_dir_path, remove_monkey_dir
+from infection_monkey.utils.monkey_log_path import get_monkey_log_path
 from infection_monkey.config import WormConfiguration
 from infection_monkey.control import ControlClient
 from infection_monkey.model import DELAY_DELETE_CMD
@@ -24,9 +25,10 @@ from infection_monkey.telemetry.trace_telem import TraceTelem
 from infection_monkey.telemetry.tunnel_telem import TunnelTelem
 from infection_monkey.windows_upgrader import WindowsUpgrader
 from infection_monkey.post_breach.post_breach_handler import PostBreach
-from common.utils.attack_utils import ScanStatus
 from infection_monkey.exploit.tools.helpers import get_interface_to_target
 from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError
+from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
+from common.utils.attack_utils import ScanStatus, UsageEnum
 
 __author__ = 'itamar'
 
@@ -91,7 +93,7 @@ class InfectionMonkey(object):
         self.set_default_port()
 
         # Create a dir for monkey files if there isn't one
-        utils.create_monkey_dir()
+        create_monkey_dir()
 
         if WindowsUpgrader.should_upgrade():
             self._upgrading_to_64 = True
@@ -103,6 +105,9 @@ class InfectionMonkey(object):
         ControlClient.wakeup(parent=self._parent)
         ControlClient.load_control_config()
 
+        if utils.is_windows_os():
+            T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send()
+
         if not WormConfiguration.alive:
             LOG.info("Marked not alive from configuration")
             return
@@ -114,7 +119,7 @@ class InfectionMonkey(object):
         if monkey_tunnel:
             monkey_tunnel.start()
 
-        StateTelem(False).send()
+        StateTelem(is_done=False).send()
         TunnelTelem().send()
 
         if WormConfiguration.collect_system_info:
@@ -226,7 +231,7 @@ class InfectionMonkey(object):
             InfectionMonkey.close_tunnel()
             firewall.close()
         else:
-            StateTelem(True).send()  # Signal the server (before closing the tunnel)
+            StateTelem(is_done=True).send()  # Signal the server (before closing the tunnel)
             InfectionMonkey.close_tunnel()
             firewall.close()
             if WormConfiguration.send_log_to_server:
@@ -245,8 +250,8 @@ class InfectionMonkey(object):
 
     @staticmethod
     def self_delete():
-        status = ScanStatus.USED if utils.remove_monkey_dir() else ScanStatus.SCANNED
-        T1107Telem(status, utils.get_monkey_dir_path()).send()
+        status = ScanStatus.USED if remove_monkey_dir() else ScanStatus.SCANNED
+        T1107Telem(status, get_monkey_dir_path()).send()
 
         if WormConfiguration.self_delete_in_cleanup \
                 and -1 == sys.executable.find('python'):
@@ -270,7 +275,7 @@ class InfectionMonkey(object):
                 T1107Telem(status, sys.executable).send()
 
     def send_log(self):
-        monkey_log_path = utils.get_monkey_log_path()
+        monkey_log_path = get_monkey_log_path()
         if os.path.exists(monkey_log_path):
             with open(monkey_log_path, 'r') as f:
                 log = f.read()
diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py
index 31ce6e24a..aaac09be2 100644
--- a/monkey/infection_monkey/network/elasticfinger.py
+++ b/monkey/infection_monkey/network/elasticfinger.py
@@ -6,11 +6,11 @@ import requests
 from requests.exceptions import Timeout, ConnectionError
 
 import infection_monkey.config
+from common.data.network_consts import ES_SERVICE
 from infection_monkey.model.host import VictimHost
 from infection_monkey.network import HostFinger
 
 ES_PORT = 9200
-ES_SERVICE = 'elastic-search-9200'
 ES_HTTP_TIMEOUT = 5
 LOG = logging.getLogger(__name__)
 __author__ = 'danielg'
diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py
index 3a9adef57..5e448002c 100644
--- a/monkey/infection_monkey/network/tools.py
+++ b/monkey/infection_monkey/network/tools.py
@@ -10,7 +10,7 @@ import re
 from six.moves import range
 
 from infection_monkey.pyinstaller_utils import get_binary_file_path
-from infection_monkey.utils import is_64bit_python
+from infection_monkey.utils.environment import is_64bit_python
 
 DEFAULT_TIMEOUT = 10
 BANNER_READ = 1024
diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py
index ff7ae3a50..09c8d4796 100644
--- a/monkey/infection_monkey/post_breach/actions/add_user.py
+++ b/monkey/infection_monkey/post_breach/actions/add_user.py
@@ -1,21 +1,16 @@
-import datetime
+from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.config import WormConfiguration
-
-
-__author__ = 'danielg'
-
-LINUX_COMMANDS = ['useradd', '-M', '--expiredate',
-                  datetime.datetime.today().strftime('%Y-%m-%d'), '--inactive', '0', '-c', 'MONKEY_USER',
-                  WormConfiguration.user_to_add]
-
-WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add,
-                    WormConfiguration.remote_user_pass,
-                    '/add', '/ACTIVE:NO']
+from infection_monkey.utils.users import get_commands_to_add_user
 
 
 class BackdoorUser(PBA):
     def __init__(self):
-        super(BackdoorUser, self).__init__("Backdoor user",
-                                           linux_cmd=' '.join(LINUX_COMMANDS),
-                                           windows_cmd=WINDOWS_COMMANDS)
+        linux_cmds, windows_cmds = get_commands_to_add_user(
+            WormConfiguration.user_to_add,
+            WormConfiguration.remote_user_pass)
+        super(BackdoorUser, self).__init__(
+            POST_BREACH_BACKDOOR_USER,
+            linux_cmd=' '.join(linux_cmds),
+            windows_cmd=windows_cmds)
+
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
new file mode 100644
index 000000000..296179d41
--- /dev/null
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -0,0 +1,143 @@
+import logging
+import os
+import random
+import string
+import subprocess
+import time
+
+import win32event
+
+from infection_monkey.utils.windows.auto_new_user import AutoNewUser, NewUserError
+from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
+from infection_monkey.post_breach.pba import PBA
+from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
+from infection_monkey.utils.environment import is_windows_os
+from infection_monkey.utils.linux.users import get_linux_commands_to_delete_user, get_linux_commands_to_add_user
+
+PING_TEST_DOMAIN = "google.com"
+
+PING_WAIT_TIMEOUT_IN_MILLISECONDS = 20 * 1000
+
+CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT = "Created process '{}' as user '{}', and successfully pinged."
+CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT = "Created process '{}' as user '{}', but failed to ping (exit status {})."
+
+USERNAME = "somenewuser"
+PASSWORD = "N3WPa55W0rD!1"
+
+logger = logging.getLogger(__name__)
+
+
+class CommunicateAsNewUser(PBA):
+    """
+    This PBA creates a new user, and then pings google as that user. This is used for a Zero Trust test of the People
+    pillar. See the relevant telemetry processing to see what findings are created.
+    """
+
+    def __init__(self):
+        super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER)
+
+    def run(self):
+        username = CommunicateAsNewUser.get_random_new_user_name()
+        if is_windows_os():
+            self.communicate_as_new_user_windows(username)
+        else:
+            self.communicate_as_new_user_linux(username)
+
+    @staticmethod
+    def get_random_new_user_name():
+        return USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
+
+    def communicate_as_new_user_linux(self, username):
+        try:
+            # add user + ping
+            linux_cmds = get_linux_commands_to_add_user(username)
+            commandline = "ping -c 1 {}".format(PING_TEST_DOMAIN)
+            linux_cmds.extend([";", "sudo", "-u", username, commandline])
+            final_command = ' '.join(linux_cmds)
+            exit_status = os.system(final_command)
+            self.send_ping_result_telemetry(exit_status, commandline, username)
+            # delete the user, async in case it gets stuck.
+            _ = subprocess.Popen(
+                get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True)
+            # Leaking the process on purpose - nothing we can do if it's stuck.
+        except subprocess.CalledProcessError as e:
+            PostBreachTelem(self, (e.output, False)).send()
+
+    def communicate_as_new_user_windows(self, username):
+        # Importing these only on windows, as they won't exist on linux.
+        import win32con
+        import win32process
+        import win32api
+
+        try:
+            with AutoNewUser(username, PASSWORD) as new_user:
+                # Using os.path is OK, as this is on windows for sure
+                ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe")
+                if not os.path.exists(ping_app_path):
+                    PostBreachTelem(self, ("{} not found.".format(ping_app_path), False)).send()
+                    return  # Can't continue without ping.
+
+                try:
+                    # Open process as that user:
+                    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
+                    commandline = "{} {} {} {}".format(ping_app_path, PING_TEST_DOMAIN, "-n", "1")
+                    process_handle, thread_handle, _, _ = win32process.CreateProcessAsUser(
+                        new_user.get_logon_handle(),  # A handle to the primary token that represents a user.
+                        None,  # The name of the module to be executed.
+                        commandline,  # The command line to be executed.
+                        None,  # Process attributes
+                        None,  # Thread attributes
+                        True,  # Should inherit handles
+                        win32con.NORMAL_PRIORITY_CLASS,  # The priority class and the creation of the process.
+                        None,  # An environment block for the new process. If this parameter is NULL, the new process
+                        # uses the environment of the calling process.
+                        None,  # CWD. If this parameter is NULL, the new process will have the same current drive and
+                        # directory as the calling process.
+                        win32process.STARTUPINFO()  # STARTUPINFO structure.
+                        # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
+                    )
+
+                    logger.debug(
+                        "Waiting for ping process to finish. Timeout: {}ms".format(PING_WAIT_TIMEOUT_IN_MILLISECONDS))
+
+                    # Ignoring return code, as we'll use `GetExitCode` to determine the state of the process later.
+                    _ = win32event.WaitForSingleObject(  # Waits until the specified object is signaled, or time-out.
+                        process_handle,  # Ping process handle
+                        PING_WAIT_TIMEOUT_IN_MILLISECONDS  # Timeout in milliseconds
+                    )
+
+                    ping_exit_code = win32process.GetExitCodeProcess(process_handle)
+
+                    self.send_ping_result_telemetry(ping_exit_code, commandline, username)
+                except Exception as e:
+                    # If failed on 1314, it's possible to try to elevate the rights of the current user with the
+                    #  "Replace a process level token" right, using Local Security Policy editing.
+                    PostBreachTelem(self, (
+                        "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
+                finally:
+                    try:
+                        win32api.CloseHandle(process_handle)
+                        win32api.CloseHandle(thread_handle)
+                    except Exception as err:
+                        logger.error("Close handle error: " + str(err))
+        except subprocess.CalledProcessError as err:
+            PostBreachTelem(self, (
+                "Couldn't create the user '{}'. Error output is: '{}'".format(username, str(err)),
+                False)).send()
+        except NewUserError as e:
+            PostBreachTelem(self, (str(e), False)).send()
+
+    def send_ping_result_telemetry(self, exit_status, commandline, username):
+        """
+        Parses the result of ping and sends telemetry accordingly.
+
+        :param exit_status: In both Windows and Linux, 0 exit code from Ping indicates success.
+        :param commandline: Exact commandline which was executed, for reporting back.
+        :param username: Username from which the command was executed, for reporting back.
+        """
+        if exit_status == 0:
+            PostBreachTelem(self, (
+                CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT.format(commandline, username), True)).send()
+        else:
+            PostBreachTelem(self, (
+                CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT.format(commandline, username, exit_status), False)).send()
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 118868d0c..89417757d 100644
--- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
+++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
@@ -1,11 +1,12 @@
 import os
 import logging
 
-from infection_monkey.utils import is_windows_os
+from common.data.post_breach_consts import POST_BREACH_FILE_EXECUTION
+from infection_monkey.utils.environment import is_windows_os
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.control import ControlClient
 from infection_monkey.config import WormConfiguration
-from infection_monkey.utils import get_monkey_dir_path
+from infection_monkey.utils.monkey_dir import get_monkey_dir_path
 from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
 from common.utils.attack_utils import ScanStatus
 from infection_monkey.exploit.tools.helpers import get_interface_to_target
@@ -27,7 +28,7 @@ class UsersPBA(PBA):
     Defines user's configured post breach action.
     """
     def __init__(self):
-        super(UsersPBA, self).__init__("Custom post breach action")
+        super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION)
         self.filename = ''
         if not is_windows_os():
             # Add linux commands to PBA's
diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py
index 926594192..8d7723df2 100644
--- a/monkey/infection_monkey/post_breach/pba.py
+++ b/monkey/infection_monkey/post_breach/pba.py
@@ -3,7 +3,7 @@ import subprocess
 
 from common.utils.attack_utils import ScanStatus
 from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
-from infection_monkey.utils import is_windows_os
+from infection_monkey.utils.environment import is_windows_os
 from infection_monkey.config import WormConfiguration
 from infection_monkey.telemetry.attack.t1064_telem import T1064Telem
 
@@ -21,7 +21,8 @@ class PBA(object):
     def __init__(self, name="unknown", linux_cmd="", windows_cmd=""):
         """
         :param name: Name of post breach action.
-        :param command: Command that will be executed on breached machine
+        :param linux_cmd: Command that will be executed on breached machine
+        :param windows_cmd: Command that will be executed on breached machine
         """
         self.command = PBA.choose_command(linux_cmd, windows_cmd)
         self.name = name
diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py
index 8522f412f..b5dfa93c7 100644
--- a/monkey/infection_monkey/post_breach/post_breach_handler.py
+++ b/monkey/infection_monkey/post_breach/post_breach_handler.py
@@ -3,7 +3,7 @@ import inspect
 import importlib
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.post_breach.actions import get_pba_files
-from infection_monkey.utils import is_windows_os
+from infection_monkey.utils.environment import is_windows_os
 
 LOG = logging.getLogger(__name__)
 
@@ -25,8 +25,12 @@ class PostBreach(object):
         Executes all post breach actions.
         """
         for pba in self.pba_list:
-            pba.run()
-        LOG.info("Post breach actions executed")
+            try:
+                LOG.debug("Executing PBA: '{}'".format(pba.name))
+                pba.run()
+            except Exception as e:
+                LOG.error("PBA {} failed. Error info: {}".format(pba.name, e))
+        LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list)))
 
     @staticmethod
     def config_to_pba_list():
@@ -45,7 +49,9 @@ class PostBreach(object):
                            if ((m[1].__module__ == module.__name__) and issubclass(m[1], PBA))]
             # Get post breach action object from class
             for pba_class in pba_classes:
+                LOG.debug("Checking if should run PBA {}".format(pba_class.__name__))
                 if pba_class.should_run(pba_class.__name__):
                     pba = pba_class()
                     pba_list.append(pba)
+                    LOG.debug("Added PBA {} to PBA list".format(pba_class.__name__))
         return pba_list
diff --git a/monkey/infection_monkey/readme.txt b/monkey/infection_monkey/readme.txt
index 0b56da2f7..06bf449da 100644
--- a/monkey/infection_monkey/readme.txt
+++ b/monkey/infection_monkey/readme.txt
@@ -62,7 +62,7 @@ a. Build sambacry binaries yourself
 	a.1. Install gcc-multilib if it's not installed
 		 sudo apt-get install gcc-multilib
 	a.2. Build the binaries
-		 cd [code location]/infection_monkey/monkey_utils/sambacry_monkey_runner
+		 cd [code location]/infection_monkey/exploit/sambacry_monkey_runner
 		 ./build.sh
 
 b. Download our pre-built sambacry binaries
diff --git a/monkey/infection_monkey/system_singleton.py b/monkey/infection_monkey/system_singleton.py
index 50fa6363b..6a4a0912b 100644
--- a/monkey/infection_monkey/system_singleton.py
+++ b/monkey/infection_monkey/system_singleton.py
@@ -4,8 +4,7 @@ import sys
 from abc import ABCMeta, abstractmethod
 
 from infection_monkey.config import WormConfiguration
-from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
-from common.utils.attack_utils import ScanStatus, UsageEnum
+
 
 __author__ = 'itamar'
 
@@ -46,21 +45,13 @@ class WindowsSystemSingleton(_SystemSingleton):
                                                      ctypes.c_char_p(self._mutex_name))
         last_error = ctypes.windll.kernel32.GetLastError()
 
-        status = None
         if not handle:
             LOG.error("Cannot acquire system singleton %r, unknown error %d",
                       self._mutex_name, last_error)
-            status = ScanStatus.SCANNED
-
+            return False
         if winerror.ERROR_ALREADY_EXISTS == last_error:
-            status = ScanStatus.SCANNED
             LOG.debug("Cannot acquire system singleton %r, mutex already exist",
                       self._mutex_name)
-
-        if not status:
-            status = ScanStatus.USED
-        T1106Telem(status, UsageEnum.SINGLETON_WINAPI).send()
-        if status == ScanStatus.SCANNED:
             return False
 
         self._mutex_handle = handle
@@ -71,7 +62,6 @@ class WindowsSystemSingleton(_SystemSingleton):
 
     def unlock(self):
         assert self._mutex_handle is not None, "Singleton not locked"
-
         ctypes.windll.kernel32.CloseHandle(self._mutex_handle)
         self._mutex_handle = None
 
diff --git a/monkey/infection_monkey/telemetry/base_telem.py b/monkey/infection_monkey/telemetry/base_telem.py
index c232ab975..31d7332bd 100644
--- a/monkey/infection_monkey/telemetry/base_telem.py
+++ b/monkey/infection_monkey/telemetry/base_telem.py
@@ -1,7 +1,11 @@
 import abc
+import json
+import logging
 
 from infection_monkey.control import ControlClient
 
+logger = logging.getLogger(__name__)
+
 __author__ = 'itay.mizeretz'
 
 
@@ -19,7 +23,9 @@ class BaseTelem(object):
         """
         Sends telemetry to island
         """
-        ControlClient.send_telemetry(self.telem_category, self.get_data())
+        data = self.get_data()
+        logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, json.dumps(data)))
+        ControlClient.send_telemetry(self.telem_category, data)
 
     @abc.abstractproperty
     def telem_category(self):
diff --git a/monkey/infection_monkey/utils.py b/monkey/infection_monkey/utils.py
deleted file mode 100644
index f8b5cc56a..000000000
--- a/monkey/infection_monkey/utils.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import os
-import shutil
-import struct
-import sys
-import tempfile
-
-from infection_monkey.config import WormConfiguration
-
-
-def get_monkey_log_path():
-    return os.path.expandvars(WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" \
-        else WormConfiguration.monkey_log_path_linux
-
-
-def get_dropper_log_path():
-    return os.path.expandvars(WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" \
-        else WormConfiguration.dropper_log_path_linux
-
-
-def is_64bit_windows_os():
-    """
-    Checks for 64 bit Windows OS using environment variables.
-    """
-    return 'PROGRAMFILES(X86)' in os.environ
-
-
-def is_64bit_python():
-    return struct.calcsize("P") == 8
-
-
-def is_windows_os():
-    return sys.platform.startswith("win")
-
-
-def utf_to_ascii(string):
-    # Converts utf string to ascii. Safe to use even if string is already ascii.
-    udata = string.decode("utf-8")
-    return udata.encode("ascii", "ignore")
-
-
-def create_monkey_dir():
-    """
-    Creates directory for monkey and related files
-    """
-    if not os.path.exists(get_monkey_dir_path()):
-        os.mkdir(get_monkey_dir_path())
-
-
-def remove_monkey_dir():
-    """
-    Removes monkey's root directory
-    :return True if removed without errors and False otherwise
-    """
-    try:
-        shutil.rmtree(get_monkey_dir_path())
-        return True
-    except Exception:
-        return False
-
-
-def get_monkey_dir_path():
-    return os.path.join(tempfile.gettempdir(), WormConfiguration.monkey_dir_name)
diff --git a/monkey/infection_monkey/utils/__init__.py b/monkey/infection_monkey/utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/infection_monkey/utils/environment.py b/monkey/infection_monkey/utils/environment.py
new file mode 100644
index 000000000..40a70ce58
--- /dev/null
+++ b/monkey/infection_monkey/utils/environment.py
@@ -0,0 +1,18 @@
+import os
+import struct
+import sys
+
+
+def is_64bit_windows_os():
+    """
+    Checks for 64 bit Windows OS using environment variables.
+    """
+    return 'PROGRAMFILES(X86)' in os.environ
+
+
+def is_64bit_python():
+    return struct.calcsize("P") == 8
+
+
+def is_windows_os():
+    return sys.platform.startswith("win")
diff --git a/monkey/infection_monkey/utils/linux/__init__.py b/monkey/infection_monkey/utils/linux/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/infection_monkey/utils/linux/users.py b/monkey/infection_monkey/utils/linux/users.py
new file mode 100644
index 000000000..1acc87d72
--- /dev/null
+++ b/monkey/infection_monkey/utils/linux/users.py
@@ -0,0 +1,21 @@
+import datetime
+
+
+def get_linux_commands_to_add_user(username):
+    return [
+        'useradd',
+        '-M',  # Do not create homedir
+        '--expiredate',
+        datetime.datetime.today().strftime('%Y-%m-%d'),
+        '--inactive',
+        '0',
+        '-c',  # Comment
+        'MONKEY_USER',  # Comment
+        username]
+
+
+def get_linux_commands_to_delete_user(username):
+    return [
+        'deluser',
+        username
+    ]
diff --git a/monkey/infection_monkey/utils/monkey_dir.py b/monkey/infection_monkey/utils/monkey_dir.py
new file mode 100644
index 000000000..bb69dae5b
--- /dev/null
+++ b/monkey/infection_monkey/utils/monkey_dir.py
@@ -0,0 +1,29 @@
+import os
+import shutil
+import tempfile
+
+from infection_monkey.config import WormConfiguration
+
+
+def create_monkey_dir():
+    """
+    Creates directory for monkey and related files
+    """
+    if not os.path.exists(get_monkey_dir_path()):
+        os.mkdir(get_monkey_dir_path())
+
+
+def remove_monkey_dir():
+    """
+    Removes monkey's root directory
+    :return True if removed without errors and False otherwise
+    """
+    try:
+        shutil.rmtree(get_monkey_dir_path())
+        return True
+    except Exception:
+        return False
+
+
+def get_monkey_dir_path():
+    return os.path.join(tempfile.gettempdir(), WormConfiguration.monkey_dir_name)
diff --git a/monkey/infection_monkey/utils/monkey_log_path.py b/monkey/infection_monkey/utils/monkey_log_path.py
new file mode 100644
index 000000000..ad80bc73d
--- /dev/null
+++ b/monkey/infection_monkey/utils/monkey_log_path.py
@@ -0,0 +1,14 @@
+import os
+import sys
+
+from infection_monkey.config import WormConfiguration
+
+
+def get_monkey_log_path():
+    return os.path.expandvars(WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" \
+        else WormConfiguration.monkey_log_path_linux
+
+
+def get_dropper_log_path():
+    return os.path.expandvars(WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" \
+        else WormConfiguration.dropper_log_path_linux
diff --git a/monkey/infection_monkey/utils/users.py b/monkey/infection_monkey/utils/users.py
new file mode 100644
index 000000000..68148d9e9
--- /dev/null
+++ b/monkey/infection_monkey/utils/users.py
@@ -0,0 +1,10 @@
+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/infection_monkey/utils/windows/__init__.py b/monkey/infection_monkey/utils/windows/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/infection_monkey/utils/windows/auto_new_user.py b/monkey/infection_monkey/utils/windows/auto_new_user.py
new file mode 100644
index 000000000..d95ac0bf0
--- /dev/null
+++ b/monkey/infection_monkey/utils/windows/auto_new_user.py
@@ -0,0 +1,69 @@
+import logging
+import subprocess
+
+from infection_monkey.post_breach.actions.add_user import BackdoorUser
+from infection_monkey.utils.windows.users import get_windows_commands_to_delete_user, get_windows_commands_to_add_user
+
+logger = logging.getLogger(__name__)
+
+
+class NewUserError(Exception):
+    pass
+
+
+class AutoNewUser(object):
+    """
+    RAII object to use for creating and using a new user in Windows. Use with `with`.
+    User will be created when the instance is instantiated.
+    User will log on at the start of the `with` scope.
+    User will log off and get deleted at the end of said `with` scope.
+
+    Example:
+             # Created                           # Logged on
+        with AutoNewUser("user", "pass") as new_user:
+            ...
+            ...
+        # Logged off and deleted
+        ...
+    """
+    def __init__(self, username, password):
+        """
+        Creates a user with the username + password.
+        :raises: subprocess.CalledProcessError if failed to add the user.
+        """
+        self.username = username
+        self.password = password
+
+        windows_cmds = get_windows_commands_to_add_user(self.username, self.password, True)
+        _ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
+
+    def __enter__(self):
+        # Importing these only on windows, as they won't exist on linux.
+        import win32security
+        import win32con
+
+        try:
+            # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
+            self.logon_handle = win32security.LogonUser(
+                self.username,
+                ".",  # Use current domain.
+                self.password,
+                win32con.LOGON32_LOGON_INTERACTIVE,  # Logon type - interactive (normal user).
+                win32con.LOGON32_PROVIDER_DEFAULT)  # Which logon provider to use - whatever Windows offers.
+        except Exception as err:
+            raise NewUserError("Can't logon as {}. Error: {}".format(self.username, str(err)))
+        return self
+
+    def get_logon_handle(self):
+        return self.logon_handle
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        # Logoff
+        self.logon_handle.Close()
+
+        # Try to delete user
+        try:
+            _ = subprocess.Popen(
+                get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True)
+        except Exception as err:
+            raise NewUserError("Can't delete user {}. Info: {}".format(self.username, err))
diff --git a/monkey/infection_monkey/utils/windows/users.py b/monkey/infection_monkey/utils/windows/users.py
new file mode 100644
index 000000000..0e6847cff
--- /dev/null
+++ b/monkey/infection_monkey/utils/windows/users.py
@@ -0,0 +1,18 @@
+def get_windows_commands_to_add_user(username, password, should_be_active=False):
+    windows_cmds = [
+        'net',
+        'user',
+        username,
+        password,
+        '/add']
+    if not should_be_active:
+        windows_cmds.append('/ACTIVE:NO')
+    return windows_cmds
+
+
+def get_windows_commands_to_delete_user(username):
+    return [
+        'net',
+        'user',
+        username,
+        '/delete']
diff --git a/monkey/infection_monkey/windows_upgrader.py b/monkey/infection_monkey/windows_upgrader.py
index 4a165940d..af904b143 100644
--- a/monkey/infection_monkey/windows_upgrader.py
+++ b/monkey/infection_monkey/windows_upgrader.py
@@ -10,7 +10,7 @@ from infection_monkey.config import WormConfiguration
 from infection_monkey.control import ControlClient
 from infection_monkey.exploit.tools.helpers import build_monkey_commandline_explicitly
 from infection_monkey.model import MONKEY_CMDLINE_WINDOWS
-from infection_monkey.utils import is_windows_os, is_64bit_windows_os, is_64bit_python
+from infection_monkey.utils.environment import is_windows_os, is_64bit_windows_os, is_64bit_python
 
 __author__ = 'itay.mizeretz'
 
diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py
index 2e04ef0be..35e932cb1 100644
--- a/monkey/monkey_island/cc/app.py
+++ b/monkey/monkey_island/cc/app.py
@@ -23,7 +23,7 @@ from monkey_island.cc.resources.monkey_download import MonkeyDownload
 from monkey_island.cc.resources.netmap import NetMap
 from monkey_island.cc.resources.node import Node
 from monkey_island.cc.resources.remote_run import RemoteRun
-from monkey_island.cc.resources.report import Report
+from monkey_island.cc.resources.reporting.report import Report
 from monkey_island.cc.resources.root import Root
 from monkey_island.cc.resources.telemetry import Telemetry
 from monkey_island.cc.resources.telemetry_feed import TelemetryFeed
@@ -122,7 +122,13 @@ def init_api_resources(api):
     api.add_resource(NetMap, '/api/netmap', '/api/netmap/')
     api.add_resource(Edge, '/api/netmap/edge', '/api/netmap/edge/')
     api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/')
-    api.add_resource(Report, '/api/report', '/api/report/')
+
+    # report_type: zero_trust or security
+    api.add_resource(
+        Report,
+        '/api/report/<string:report_type>',
+        '/api/report/<string:report_type>/<string:report_data>')
+
     api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
     api.add_resource(Log, '/api/log', '/api/log/')
     api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/')
diff --git a/monkey/monkey_island/cc/environment/testing.py b/monkey/monkey_island/cc/environment/testing.py
index 286e442dd..087c3a2e3 100644
--- a/monkey/monkey_island/cc/environment/testing.py
+++ b/monkey/monkey_island/cc/environment/testing.py
@@ -1,5 +1,4 @@
 from monkey_island.cc.environment import Environment
-import monkey_island.cc.auth
 
 
 class TestingEnvironment(Environment):
@@ -7,11 +6,5 @@ class TestingEnvironment(Environment):
         super(TestingEnvironment, self).__init__()
         self.testing = True
 
-    # SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()'
-    NO_AUTH_CREDS = '55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062' \
-                    '8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557'
-
     def get_auth_users(self):
-        return [
-            monkey_island.cc.auth.User(1, self.NO_AUTH_CREDS, self.NO_AUTH_CREDS)
-        ]
+        return []
diff --git a/monkey/monkey_island/cc/models/zero_trust/__init__.py b/monkey/monkey_island/cc/models/zero_trust/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py
new file mode 100644
index 000000000..c3ed52649
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py
@@ -0,0 +1,32 @@
+from common.data.zero_trust_consts import TEST_MALICIOUS_ACTIVITY_TIMELINE, STATUS_VERIFY
+from monkey_island.cc.models.zero_trust.finding import Finding
+
+
+class AggregateFinding(Finding):
+    @staticmethod
+    def create_or_add_to_existing(test, status, events):
+        """
+        Create a new finding or add the events to an existing one if it's the same (same meaning same status and same
+        test).
+
+        :raises: Assertion error if this is used when there's more then one finding which fits the query - this is not
+        when this function should be used.
+        """
+        existing_findings = Finding.objects(test=test, status=status)
+        assert (len(existing_findings) < 2), "More than one finding exists for {}:{}".format(test, status)
+
+        if len(existing_findings) == 0:
+            Finding.save_finding(test, status, events)
+        else:
+            # Now we know for sure this is the only one
+            orig_finding = existing_findings[0]
+            orig_finding.add_events(events)
+            orig_finding.save()
+
+
+def add_malicious_activity_to_timeline(events):
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
+        status=STATUS_VERIFY,
+        events=events
+    )
diff --git a/monkey/monkey_island/cc/models/zero_trust/event.py b/monkey/monkey_island/cc/models/zero_trust/event.py
new file mode 100644
index 000000000..6ad728d66
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/event.py
@@ -0,0 +1,36 @@
+from datetime import datetime
+
+from mongoengine import EmbeddedDocument, DateTimeField, StringField
+
+from common.data.zero_trust_consts import EVENT_TYPES
+
+
+class Event(EmbeddedDocument):
+    """
+    This model represents a single event within a Finding (it is an EmbeddedDocument within Finding). It is meant to
+    hold a detail of the Finding.
+
+    This class has 2 main section:
+        *   The schema section defines the DB fields in the document. This is the data of the object.
+        *   The logic section defines complex questions we can ask about a single document which are asked multiple
+            times, or complex action we will perform - somewhat like an API.
+    """
+    # SCHEMA
+    timestamp = DateTimeField(required=True)
+    title = StringField(required=True)
+    message = StringField()
+    event_type = StringField(required=True, choices=EVENT_TYPES)
+
+    # LOGIC
+    @staticmethod
+    def create_event(title, message, event_type, timestamp=datetime.now()):
+        event = Event(
+            timestamp=timestamp,
+            title=title,
+            message=message,
+            event_type=event_type
+        )
+
+        event.validate(clean=True)
+
+        return event
diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py
new file mode 100644
index 000000000..df4eb12f7
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/finding.py
@@ -0,0 +1,60 @@
+# coding=utf-8
+"""
+Define a Document Schema for Zero Trust findings.
+"""
+
+from mongoengine import Document, StringField, EmbeddedDocumentListField
+
+from common.data.zero_trust_consts import ORDERED_TEST_STATUSES, TESTS, TESTS_MAP, TEST_EXPLANATION_KEY, PILLARS_KEY
+# Dummy import for mongoengine.
+# noinspection PyUnresolvedReferences
+from monkey_island.cc.models.zero_trust.event import Event
+
+
+class Finding(Document):
+    """
+    This model represents a Zero-Trust finding: A result of a test the monkey/island might perform to see if a
+    specific principle of zero trust is upheld or broken.
+
+    Findings might have the following statuses:
+        Failed ❌
+            Meaning that we are sure that something is wrong (example: segmentation issue).
+        Verify ⁉
+            Meaning that we need the user to check something himself (example: 2FA logs, AV missing).
+        Passed ✔
+            Meaning that we are sure that something is correct (example: Monkey failed exploiting).
+
+    This class has 2 main section:
+        *   The schema section defines the DB fields in the document. This is the data of the object.
+        *   The logic section defines complex questions we can ask about a single document which are asked multiple
+            times, or complex action we will perform - somewhat like an API.
+    """
+    # SCHEMA
+    test = StringField(required=True, choices=TESTS)
+    status = StringField(required=True, choices=ORDERED_TEST_STATUSES)
+    events = EmbeddedDocumentListField(document_type=Event)
+    # http://docs.mongoengine.org/guide/defining-documents.html#document-inheritance
+    meta = {'allow_inheritance': True}
+
+    # LOGIC
+    def get_test_explanation(self):
+        return TESTS_MAP[self.test][TEST_EXPLANATION_KEY]
+
+    def get_pillars(self):
+        return TESTS_MAP[self.test][PILLARS_KEY]
+
+    # Creation methods
+    @staticmethod
+    def save_finding(test, status, events):
+        finding = Finding(
+            test=test,
+            status=status,
+            events=events)
+
+        finding.save()
+
+        return finding
+
+    def add_events(self, events):
+        # type: (list) -> None
+        self.events.extend(events)
diff --git a/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py
new file mode 100644
index 000000000..32a450f57
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py
@@ -0,0 +1,50 @@
+from mongoengine import StringField
+
+from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_FAILED, STATUS_PASSED
+from monkey_island.cc.models.zero_trust.finding import Finding
+
+
+def need_to_overwrite_status(saved_status, new_status):
+    return (saved_status == STATUS_PASSED) and (new_status == STATUS_FAILED)
+
+
+class SegmentationFinding(Finding):
+    first_subnet = StringField()
+    second_subnet = StringField()
+
+    @staticmethod
+    def create_or_add_to_existing_finding(subnets, status, segmentation_event):
+        """
+        Creates a segmentation finding. If a segmentation finding with the relevant subnets already exists, adds the
+        event to the existing finding, and the "worst" status is chosen (i.e. if the existing one is "Failed" it will
+        remain so).   
+
+        :param subnets: the 2 subnets of this finding.
+        :param status: STATUS_PASSED or STATUS_FAILED
+        :param segmentation_event: The specific event
+        """
+        assert len(subnets) == 2
+
+        # Sort them so A -> B and B -> A segmentation findings will be the same one.
+        subnets.sort()
+
+        existing_findings = SegmentationFinding.objects(first_subnet=subnets[0], second_subnet=subnets[1])
+
+        if len(existing_findings) == 0:
+            # No finding exists - create.
+            new_finding = SegmentationFinding(
+                first_subnet=subnets[0],
+                second_subnet=subnets[1],
+                test=TEST_SEGMENTATION,
+                status=status,
+                events=[segmentation_event]
+            )
+            new_finding.save()
+        else:
+            # A finding exists (should be one). Add the event to it.
+            assert len(existing_findings) == 1
+            existing_finding = existing_findings[0]
+            existing_finding.events.append(segmentation_event)
+            if need_to_overwrite_status(existing_finding.status, status):
+                existing_finding.status = status
+            existing_finding.save()
diff --git a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
new file mode 100644
index 000000000..c1a94166f
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
@@ -0,0 +1,53 @@
+from common.data.zero_trust_consts import *
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.models.zero_trust.finding import Finding
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+
+
+class TestAggregateFinding(IslandTestCase):
+    def test_create_or_add_to_existing(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        test = TEST_MALICIOUS_ACTIVITY_TIMELINE
+        status = STATUS_VERIFY
+        events = [Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK)]
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 0)
+
+        AggregateFinding.create_or_add_to_existing(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 1)
+        self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 1)
+
+        AggregateFinding.create_or_add_to_existing(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 1)
+        self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 2)
+
+    def test_create_or_add_to_existing_2_tests_already_exist(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        test = TEST_MALICIOUS_ACTIVITY_TIMELINE
+        status = STATUS_VERIFY
+        event = Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK)
+        events = [event]
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 0)
+
+        Finding.save_finding(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 1)
+        self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 1)
+
+        AggregateFinding.create_or_add_to_existing(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 1)
+        self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 2)
+
+        Finding.save_finding(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 2)
+
+        with self.assertRaises(AssertionError):
+            AggregateFinding.create_or_add_to_existing(test, status, events)
diff --git a/monkey/monkey_island/cc/models/zero_trust/test_event.py b/monkey/monkey_island/cc/models/zero_trust/test_event.py
new file mode 100644
index 000000000..c0742407d
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/test_event.py
@@ -0,0 +1,32 @@
+from mongoengine import ValidationError
+
+from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+
+
+class TestEvent(IslandTestCase):
+    def test_create_event(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        with self.assertRaises(ValidationError):
+            _ = Event.create_event(
+                title=None,  # title required
+                message="bla bla",
+                event_type=EVENT_TYPE_MONKEY_NETWORK
+            )
+
+        with self.assertRaises(ValidationError):
+            _ = Event.create_event(
+                title="skjs",
+                message="bla bla",
+                event_type="Unknown"  # Unknown event type
+            )
+
+        # Assert that nothing is raised.
+        _ = Event.create_event(
+            title="skjs",
+            message="bla bla",
+            event_type=EVENT_TYPE_MONKEY_NETWORK
+        )
diff --git a/monkey/monkey_island/cc/models/zero_trust/test_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_finding.py
new file mode 100644
index 000000000..88a33d5d3
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/test_finding.py
@@ -0,0 +1,38 @@
+from mongoengine import ValidationError
+
+from common.data.zero_trust_consts import *
+from monkey_island.cc.models.zero_trust.finding import Finding
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+
+
+class TestFinding(IslandTestCase):
+    """
+    Make sure to set server environment to `testing` in server.json! Otherwise this will mess up your mongo instance and
+    won't work.
+
+    Also, the working directory needs to be the working directory from which you usually run the island so the
+    server.json file is found and loaded.
+    """
+    def test_save_finding_validation(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        with self.assertRaises(ValidationError):
+            _ = Finding.save_finding(test="bla bla", status=STATUS_FAILED, events=[])
+
+        with self.assertRaises(ValidationError):
+            _ = Finding.save_finding(test=TEST_SEGMENTATION, status="bla bla", events=[])
+
+    def test_save_finding_sanity(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 0)
+
+        event_example = Event.create_event(
+            title="Event Title", message="event message", event_type=EVENT_TYPE_MONKEY_NETWORK)
+        Finding.save_finding(test=TEST_SEGMENTATION, status=STATUS_FAILED, events=[event_example])
+
+        self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 1)
+        self.assertEquals(len(Finding.objects(status=STATUS_FAILED)), 1)
diff --git a/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py
new file mode 100644
index 000000000..80e564a17
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py
@@ -0,0 +1,52 @@
+from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding
+
+
+class TestSegmentationFinding(IslandTestCase):
+    def test_create_or_add_to_existing_finding(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        first_segment = "1.1.1.0/24"
+        second_segment = "2.2.2.0-2.2.2.254"
+        third_segment = "3.3.3.3"
+        event = Event.create_event("bla", "bla", EVENT_TYPE_MONKEY_NETWORK)
+
+        SegmentationFinding.create_or_add_to_existing_finding(
+            subnets=[first_segment, second_segment],
+            status=STATUS_FAILED,
+            segmentation_event=event
+        )
+
+        self.assertEquals(len(SegmentationFinding.objects()), 1)
+        self.assertEquals(len(SegmentationFinding.objects()[0].events), 1)
+
+        SegmentationFinding.create_or_add_to_existing_finding(
+            # !!! REVERSE ORDER
+            subnets=[second_segment, first_segment],
+            status=STATUS_FAILED,
+            segmentation_event=event
+        )
+
+        self.assertEquals(len(SegmentationFinding.objects()), 1)
+        self.assertEquals(len(SegmentationFinding.objects()[0].events), 2)
+
+        SegmentationFinding.create_or_add_to_existing_finding(
+            # !!! REVERSE ORDER
+            subnets=[first_segment, third_segment],
+            status=STATUS_FAILED,
+            segmentation_event=event
+        )
+
+        self.assertEquals(len(SegmentationFinding.objects()), 2)
+
+        SegmentationFinding.create_or_add_to_existing_finding(
+            # !!! REVERSE ORDER
+            subnets=[second_segment, third_segment],
+            status=STATUS_FAILED,
+            segmentation_event=event
+        )
+
+        self.assertEquals(len(SegmentationFinding.objects()), 3)
diff --git a/monkey/monkey_island/cc/resources/report.py b/monkey/monkey_island/cc/resources/report.py
deleted file mode 100644
index 62a014fef..000000000
--- a/monkey/monkey_island/cc/resources/report.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import flask_restful
-
-from monkey_island.cc.auth import jwt_required
-from monkey_island.cc.services.report import ReportService
-
-__author__ = "itay.mizeretz"
-
-
-class Report(flask_restful.Resource):
-
-    @jwt_required()
-    def get(self):
-        return ReportService.get_report()
diff --git a/monkey/monkey_island/cc/resources/reporting/__init__.py b/monkey/monkey_island/cc/resources/reporting/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py
new file mode 100644
index 000000000..8c5286fee
--- /dev/null
+++ b/monkey/monkey_island/cc/resources/reporting/report.py
@@ -0,0 +1,41 @@
+import httplib
+
+
+import flask_restful
+from flask import jsonify
+
+from monkey_island.cc.auth import jwt_required
+from monkey_island.cc.services.reporting.report import ReportService
+from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService
+
+ZERO_TRUST_REPORT_TYPE = "zero_trust"
+SECURITY_REPORT_TYPE = "security"
+REPORT_TYPES = [SECURITY_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE]
+
+REPORT_DATA_PILLARS = "pillars"
+REPORT_DATA_FINDINGS = "findings"
+REPORT_DATA_PRINCIPLES_STATUS = "principles"
+
+__author__ = ["itay.mizeretz", "shay.nehmad"]
+
+
+class Report(flask_restful.Resource):
+
+    @jwt_required()
+    def get(self, report_type=SECURITY_REPORT_TYPE, report_data=None):
+        if report_type == SECURITY_REPORT_TYPE:
+            return ReportService.get_report()
+        elif report_type == ZERO_TRUST_REPORT_TYPE:
+            if report_data == REPORT_DATA_PILLARS:
+                return jsonify({
+                        "statusesToPillars": ZeroTrustService.get_statuses_to_pillars(),
+                        "pillarsToStatuses": ZeroTrustService.get_pillars_to_statuses(),
+                        "grades": ZeroTrustService.get_pillars_grades()
+                    }
+                )
+            elif report_data == REPORT_DATA_PRINCIPLES_STATUS:
+                return jsonify(ZeroTrustService.get_principles_status())
+            elif report_data == REPORT_DATA_FINDINGS:
+                return jsonify(ZeroTrustService.get_all_findings())
+
+        flask_restful.abort(httplib.NOT_FOUND)
diff --git a/monkey/monkey_island/cc/resources/root.py b/monkey/monkey_island/cc/resources/root.py
index 2af73a45e..e3b3e9854 100644
--- a/monkey/monkey_island/cc/resources/root.py
+++ b/monkey/monkey_island/cc/resources/root.py
@@ -7,7 +7,7 @@ from flask import request, make_response, jsonify
 from monkey_island.cc.auth import jwt_required
 from monkey_island.cc.database import mongo
 from monkey_island.cc.services.node import NodeService
-from monkey_island.cc.services.report import ReportService
+from monkey_island.cc.services.reporting.report import ReportService
 from monkey_island.cc.services.attack.attack_report import AttackReportService
 from monkey_island.cc.utils import local_ip_addresses
 from monkey_island.cc.services.database import Database
diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py
index fa942d174..dc6a7d512 100644
--- a/monkey/monkey_island/cc/resources/telemetry.py
+++ b/monkey/monkey_island/cc/resources/telemetry.py
@@ -1,6 +1,5 @@
 import json
 import logging
-import copy
 from datetime import datetime
 
 import dateutil
@@ -9,12 +8,8 @@ from flask import request
 
 from monkey_island.cc.auth import jwt_required
 from monkey_island.cc.database import mongo
-from monkey_island.cc.services import mimikatz_utils
-from monkey_island.cc.services.config import ConfigService
-from monkey_island.cc.services.edge import EdgeService
 from monkey_island.cc.services.node import NodeService
-from monkey_island.cc.encryptor import encryptor
-from monkey_island.cc.services.wmi_handler import WMIHandler
+from monkey_island.cc.services.telemetry.processing.processing import process_telemetry
 from monkey_island.cc.models.monkey import Monkey
 
 __author__ = 'Barak'
@@ -54,16 +49,9 @@ class Telemetry(flask_restful.Resource):
         Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']).renew_ttl()
 
         monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
+        NodeService.update_monkey_modify_time(monkey["_id"])
 
-        try:
-            NodeService.update_monkey_modify_time(monkey["_id"])
-            telem_category = telemetry_json.get('telem_category')
-            if telem_category in TELEM_PROCESS_DICT:
-                TELEM_PROCESS_DICT[telem_category](telemetry_json)
-            else:
-                logger.info('Got unknown type of telemetry: %s' % telem_category)
-        except Exception as ex:
-            logger.error("Exception caught while processing telemetry. Info: {}".format(ex.message), exc_info=True)
+        process_telemetry(telemetry_json)
 
         telem_id = mongo.db.telemetry.insert(telemetry_json)
         return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
@@ -90,200 +78,3 @@ class Telemetry(flask_restful.Resource):
                         x['data']['credentials'][new_user] = x['data']['credentials'].pop(user)
 
         return objects
-
-    @staticmethod
-    def get_edge_by_scan_or_exploit_telemetry(telemetry_json):
-        dst_ip = telemetry_json['data']['machine']['ip_addr']
-        dst_domain_name = telemetry_json['data']['machine']['domain_name']
-        src_monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
-        dst_node = NodeService.get_monkey_by_ip(dst_ip)
-        if dst_node is None:
-            dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name)
-
-        return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"])
-
-    @staticmethod
-    def process_tunnel_telemetry(telemetry_json):
-        monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"]
-        if telemetry_json['data']['proxy'] is not None:
-            tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "")
-            NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip)
-        else:
-            NodeService.unset_all_monkey_tunnels(monkey_id)
-
-    @staticmethod
-    def process_state_telemetry(telemetry_json):
-        monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
-        NodeService.add_communication_info(monkey, telemetry_json['command_control_channel'])
-        if telemetry_json['data']['done']:
-            NodeService.set_monkey_dead(monkey, True)
-        else:
-            NodeService.set_monkey_dead(monkey, False)
-
-    @staticmethod
-    def process_exploit_telemetry(telemetry_json):
-        edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
-        Telemetry.encrypt_exploit_creds(telemetry_json)
-        telemetry_json['data']['info']['started'] = dateutil.parser.parse(telemetry_json['data']['info']['started'])
-        telemetry_json['data']['info']['finished'] = dateutil.parser.parse(telemetry_json['data']['info']['finished'])
-
-        new_exploit = copy.deepcopy(telemetry_json['data'])
-
-        new_exploit.pop('machine')
-        new_exploit['timestamp'] = telemetry_json['timestamp']
-
-        mongo.db.edge.update(
-            {'_id': edge['_id']},
-            {'$push': {'exploits': new_exploit}}
-        )
-        if new_exploit['result']:
-            EdgeService.set_edge_exploited(edge)
-
-        for attempt in telemetry_json['data']['attempts']:
-            if attempt['result']:
-                found_creds = {'user': attempt['user']}
-                for field in ['password', 'lm_hash', 'ntlm_hash', 'ssh_key']:
-                    if len(attempt[field]) != 0:
-                        found_creds[field] = attempt[field]
-                NodeService.add_credentials_to_node(edge['to'], found_creds)
-
-    @staticmethod
-    def process_scan_telemetry(telemetry_json):
-        edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
-        data = copy.deepcopy(telemetry_json['data']['machine'])
-        ip_address = data.pop("ip_addr")
-        domain_name = data.pop("domain_name")
-        new_scan = \
-            {
-                "timestamp": telemetry_json["timestamp"],
-                "data": data
-            }
-        mongo.db.edge.update(
-            {"_id": edge["_id"]},
-            {"$push": {"scans": new_scan},
-             "$set": {"ip_address": ip_address, 'domain_name': domain_name}}
-        )
-
-        node = mongo.db.node.find_one({"_id": edge["to"]})
-        if node is not None:
-            scan_os = new_scan["data"]["os"]
-            if "type" in scan_os:
-                mongo.db.node.update({"_id": node["_id"]},
-                                     {"$set": {"os.type": scan_os["type"]}},
-                                     upsert=False)
-            if "version" in scan_os:
-                mongo.db.node.update({"_id": node["_id"]},
-                                     {"$set": {"os.version": scan_os["version"]}},
-                                     upsert=False)
-
-    @staticmethod
-    def process_system_info_telemetry(telemetry_json):
-        users_secrets = {}
-        monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id')
-        if 'ssh_info' in telemetry_json['data']:
-            ssh_info = telemetry_json['data']['ssh_info']
-            Telemetry.encrypt_system_info_ssh_keys(ssh_info)
-            if telemetry_json['data']['network_info']['networks']:
-                # We use user_name@machine_ip as the name of the ssh key stolen, thats why we need ip from telemetry
-                Telemetry.add_ip_to_ssh_keys(telemetry_json['data']['network_info']['networks'][0], ssh_info)
-            Telemetry.add_system_info_ssh_keys_to_config(ssh_info)
-        if 'credentials' in telemetry_json['data']:
-            creds = telemetry_json['data']['credentials']
-            Telemetry.encrypt_system_info_creds(creds)
-            Telemetry.add_system_info_creds_to_config(creds)
-            Telemetry.replace_user_dot_with_comma(creds)
-        if 'mimikatz' in telemetry_json['data']:
-            users_secrets = mimikatz_utils.MimikatzSecrets. \
-                extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', ''))
-        if 'wmi' in telemetry_json['data']:
-            wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)
-            wmi_handler.process_and_handle_wmi_info()
-        if 'aws' in telemetry_json['data']:
-            if 'instance_id' in telemetry_json['data']['aws']:
-                mongo.db.monkey.update_one({'_id': monkey_id},
-                                           {'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}})
-
-    @staticmethod
-    def add_ip_to_ssh_keys(ip, ssh_info):
-        for key in ssh_info:
-            key['ip'] = ip['addr']
-
-    @staticmethod
-    def process_trace_telemetry(telemetry_json):
-        # Nothing to do
-        return
-
-    @staticmethod
-    def replace_user_dot_with_comma(creds):
-        for user in creds:
-            if -1 != user.find('.'):
-                new_user = user.replace('.', ',')
-                creds[new_user] = creds.pop(user)
-
-    @staticmethod
-    def encrypt_system_info_creds(creds):
-        for user in creds:
-            for field in ['password', 'lm_hash', 'ntlm_hash']:
-                if field in creds[user]:
-                    # this encoding is because we might run into passwords which are not pure ASCII
-                    creds[user][field] = encryptor.enc(creds[user][field].encode('utf-8'))
-
-    @staticmethod
-    def encrypt_system_info_ssh_keys(ssh_info):
-        for idx, user in enumerate(ssh_info):
-            for field in ['public_key', 'private_key', 'known_hosts']:
-                if ssh_info[idx][field]:
-                    ssh_info[idx][field] = encryptor.enc(ssh_info[idx][field].encode('utf-8'))
-
-    @staticmethod
-    def add_system_info_creds_to_config(creds):
-        for user in creds:
-            ConfigService.creds_add_username(user)
-            if 'password' in creds[user]:
-                ConfigService.creds_add_password(creds[user]['password'])
-            if 'lm_hash' in creds[user]:
-                ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
-            if 'ntlm_hash' in creds[user]:
-                ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
-
-    @staticmethod
-    def add_system_info_ssh_keys_to_config(ssh_info):
-        for user in ssh_info:
-            ConfigService.creds_add_username(user['name'])
-            # Public key is useless without private key
-            if user['public_key'] and user['private_key']:
-                ConfigService.ssh_add_keys(user['public_key'], user['private_key'],
-                                           user['name'], user['ip'])
-
-    @staticmethod
-    def encrypt_exploit_creds(telemetry_json):
-        attempts = telemetry_json['data']['attempts']
-        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] = encryptor.enc(credential.encode('utf-8'))
-
-    @staticmethod
-    def process_post_breach_telemetry(telemetry_json):
-        mongo.db.monkey.update(
-            {'guid': telemetry_json['monkey_guid']},
-            {'$push': {'pba_results': telemetry_json['data']}})
-
-    @staticmethod
-    def process_attack_telemetry(telemetry_json):
-        # No processing required
-        pass
-
-
-TELEM_PROCESS_DICT = \
-    {
-        'tunnel': Telemetry.process_tunnel_telemetry,
-        'state': Telemetry.process_state_telemetry,
-        'exploit': Telemetry.process_exploit_telemetry,
-        'scan': Telemetry.process_scan_telemetry,
-        'system_info': Telemetry.process_system_info_telemetry,
-        'trace': Telemetry.process_trace_telemetry,
-        'post_breach': Telemetry.process_post_breach_telemetry,
-        'attack': Telemetry.process_attack_telemetry
-    }
diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py
index 4ef418b6c..f3a6c9fac 100644
--- a/monkey/monkey_island/cc/services/config_schema.py
+++ b/monkey/monkey_island/cc/services/config_schema.py
@@ -111,6 +111,14 @@ SCHEMA = {
                     "title": "Back door user",
                     "attack_techniques": []
                 },
+                {
+                    "type": "string",
+                    "enum": [
+                        "CommunicateAsNewUser"
+                    ],
+                    "title": "Communicate as new user",
+                    "attack_techniques": []
+                },
             ],
         },
         "finger_classes": {
@@ -329,6 +337,7 @@ SCHEMA = {
                                 "$ref": "#/definitions/post_breach_acts"
                             },
                             "default": [
+                                "CommunicateAsNewUser"
                             ],
                             "description": "List of actions the Monkey will run post breach"
                         },
diff --git a/monkey/monkey_island/cc/services/configuration/__init__.py b/monkey/monkey_island/cc/services/configuration/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/monkey_island/cc/services/configuration/utils.py b/monkey/monkey_island/cc/services/configuration/utils.py
new file mode 100644
index 000000000..34d6a9bb5
--- /dev/null
+++ b/monkey/monkey_island/cc/services/configuration/utils.py
@@ -0,0 +1,5 @@
+from monkey_island.cc.services.config import ConfigService
+
+
+def get_config_network_segments_as_subnet_groups():
+    return [ConfigService.get_config_value(['basic_network', 'network_analysis', 'inaccessible_subnets'])]
diff --git a/monkey/monkey_island/cc/services/pth_report.py b/monkey/monkey_island/cc/services/reporting/pth_report.py
similarity index 100%
rename from monkey/monkey_island/cc/services/pth_report.py
rename to monkey/monkey_island/cc/services/reporting/pth_report.py
diff --git a/monkey/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/reporting/report.py
similarity index 96%
rename from monkey/monkey_island/cc/services/report.py
rename to monkey/monkey_island/cc/services/reporting/report.py
index 409586e66..f00fbc22c 100644
--- a/monkey/monkey_island/cc/services/report.py
+++ b/monkey/monkey_island/cc/services/reporting/report.py
@@ -9,14 +9,16 @@ from enum import Enum
 
 from six import text_type
 
+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.services.reporting.report_exporter_manager import ReportExporterManager
 from monkey_island.cc.services.config import ConfigService
+from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups
 from monkey_island.cc.services.edge import EdgeService
 from monkey_island.cc.services.node import NodeService
 from monkey_island.cc.utils import local_ip_addresses, get_subnets
-from monkey_island.cc.services.pth_report import PTHReportService
+from monkey_island.cc.services.reporting.pth_report import PTHReportService
 from common.network.network_range import NetworkRange
 
 __author__ = "itay.mizeretz"
@@ -415,23 +417,6 @@ class ReportService:
 
         return issues
 
-    @staticmethod
-    def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet):
-        """
-        Finds an IP address in ip_addresses which is in source_subnet but not in target_subnet.
-        :param ip_addresses:    List of IP addresses to test.
-        :param source_subnet:   Subnet to want an IP to not be in.
-        :param target_subnet:   Subnet we want an IP to be in.
-        :return:
-        """
-        for ip_address in ip_addresses:
-            if target_subnet.is_in_range(ip_address):
-                return None
-        for ip_address in ip_addresses:
-            if source_subnet.is_in_range(ip_address):
-                return ip_address
-        return None
-
     @staticmethod
     def get_cross_segment_issues_of_single_machine(source_subnet_range, target_subnet_range):
         """
@@ -494,9 +479,9 @@ class ReportService:
             target_ip = scan['data']['machine']['ip_addr']
             if target_subnet_range.is_in_range(text_type(target_ip)):
                 monkey = NodeService.get_monkey_by_guid(scan['monkey_guid'])
-                cross_segment_ip = ReportService.get_ip_in_src_and_not_in_dst(monkey['ip_addresses'],
-                                                                              source_subnet_range,
-                                                                              target_subnet_range)
+                cross_segment_ip = get_ip_in_src_and_not_in_dst(monkey['ip_addresses'],
+                                                                source_subnet_range,
+                                                                target_subnet_range)
 
                 if cross_segment_ip is not None:
                     cross_segment_issues.append(
@@ -544,7 +529,7 @@ class ReportService:
         cross_segment_issues = []
 
         # For now the feature is limited to 1 group.
-        subnet_groups = [ConfigService.get_config_value(['basic_network', 'network_analysis', 'inaccessible_subnets'])]
+        subnet_groups = get_config_network_segments_as_subnet_groups()
 
         for subnet_group in subnet_groups:
             cross_segment_issues += ReportService.get_cross_segment_issues_per_subnet_group(scans, subnet_group)
diff --git a/monkey/monkey_island/cc/services/pth_report_test.py b/monkey/monkey_island/cc/services/reporting/test_pth_report.py
similarity index 96%
rename from monkey/monkey_island/cc/services/pth_report_test.py
rename to monkey/monkey_island/cc/services/reporting/test_pth_report.py
index 24e560ff0..f934f50ab 100644
--- a/monkey/monkey_island/cc/services/pth_report_test.py
+++ b/monkey/monkey_island/cc/services/reporting/test_pth_report.py
@@ -1,7 +1,7 @@
 import uuid
 
 from monkey_island.cc.models import Monkey
-from monkey_island.cc.services.pth_report import PTHReportService
+from monkey_island.cc.services.reporting.pth_report import PTHReportService
 from monkey_island.cc.testing.IslandTestCase import IslandTestCase
 
 
diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py
new file mode 100644
index 000000000..46b4fefd7
--- /dev/null
+++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py
@@ -0,0 +1,285 @@
+from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService
+
+from common.data.zero_trust_consts import *
+from monkey_island.cc.models.zero_trust.finding import Finding
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+
+
+def save_example_findings():
+    # arrange
+    Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_PASSED, [])  # devices passed = 1
+    Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_PASSED, [])  # devices passed = 2
+    Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_FAILED, [])  # devices failed = 1
+    # devices unexecuted = 1
+    # people verify = 1
+    # networks verify = 1
+    Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_VERIFY, [])
+    # people verify = 2
+    # networks verify = 2
+    Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_VERIFY, [])
+    # data failed 1
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, [])
+    # data failed 2
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, [])
+    # data failed 3
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, [])
+    # data failed 4
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, [])
+    # data failed 5
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, [])
+    # data verify 1
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_VERIFY, [])
+    # data verify 2
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_VERIFY, [])
+    # data passed 1
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_PASSED, [])
+
+
+class TestZeroTrustService(IslandTestCase):
+    def test_get_pillars_grades(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        save_example_findings()
+
+        expected = [
+            {
+                STATUS_FAILED: 5,
+                STATUS_VERIFY: 2,
+                STATUS_PASSED: 1,
+                STATUS_UNEXECUTED: 1,
+                "pillar": "Data"
+            },
+            {
+                STATUS_FAILED: 0,
+                STATUS_VERIFY: 2,
+                STATUS_PASSED: 0,
+                STATUS_UNEXECUTED: 1,
+                "pillar": "People"
+            },
+            {
+                STATUS_FAILED: 0,
+                STATUS_VERIFY: 2,
+                STATUS_PASSED: 0,
+                STATUS_UNEXECUTED: 4,
+                "pillar": "Networks"
+            },
+            {
+                STATUS_FAILED: 1,
+                STATUS_VERIFY: 0,
+                STATUS_PASSED: 2,
+                STATUS_UNEXECUTED: 1,
+                "pillar": "Devices"
+            },
+            {
+                STATUS_FAILED: 0,
+                STATUS_VERIFY: 0,
+                STATUS_PASSED: 0,
+                STATUS_UNEXECUTED: 0,
+                "pillar": "Workloads"
+            },
+            {
+                STATUS_FAILED: 0,
+                STATUS_VERIFY: 0,
+                STATUS_PASSED: 0,
+                STATUS_UNEXECUTED: 3,
+                "pillar": "Visibility & Analytics"
+            },
+            {
+                STATUS_FAILED: 0,
+                STATUS_VERIFY: 0,
+                STATUS_PASSED: 0,
+                STATUS_UNEXECUTED: 0,
+                "pillar": "Automation & Orchestration"
+            }
+        ]
+
+        result = ZeroTrustService.get_pillars_grades()
+
+        self.assertEquals(result, expected)
+
+    def test_get_principles_status(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        self.maxDiff = None
+
+        save_example_findings()
+
+        expected = {
+            AUTOMATION_ORCHESTRATION: [],
+            DATA: [
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_DATA_TRANSIT],
+                    "status": STATUS_FAILED,
+                    "tests": [
+                        {
+                            "status": STATUS_FAILED,
+                            "test": TESTS_MAP[TEST_DATA_ENDPOINT_HTTP][TEST_EXPLANATION_KEY]
+                        },
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_DATA_ENDPOINT_ELASTIC][TEST_EXPLANATION_KEY]
+                        },
+                    ]
+                }
+            ],
+            DEVICES: [
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_ENDPOINT_SECURITY],
+                    "status": STATUS_FAILED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_MACHINE_EXPLOITED][TEST_EXPLANATION_KEY]
+                        },
+                        {
+                            "status": STATUS_FAILED,
+                            "test": TESTS_MAP[TEST_ENDPOINT_SECURITY_EXISTS][TEST_EXPLANATION_KEY]
+                        },
+                    ]
+                }
+            ],
+            NETWORKS: [
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_SEGMENTATION],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_SEGMENTATION][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_USER_BEHAVIOUR],
+                    "status": STATUS_VERIFY,
+                    "tests": [
+                        {
+                            "status": STATUS_VERIFY,
+                            "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_TUNNELING][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+            ],
+            PEOPLE: [
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_USER_BEHAVIOUR],
+                    "status": STATUS_VERIFY,
+                    "tests": [
+                        {
+                            "status": STATUS_VERIFY,
+                            "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                }
+            ],
+            VISIBILITY_ANALYTICS: [
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_TUNNELING][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
+            ],
+            WORKLOADS: []
+        }
+
+        result = ZeroTrustService.get_principles_status()
+        self.assertEquals(result, expected)
+
+    def test_get_pillars_to_statuses(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        self.maxDiff = None
+
+        expected = {
+            AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED,
+            DEVICES: STATUS_UNEXECUTED,
+            NETWORKS: STATUS_UNEXECUTED,
+            PEOPLE: STATUS_UNEXECUTED,
+            VISIBILITY_ANALYTICS: STATUS_UNEXECUTED,
+            WORKLOADS: STATUS_UNEXECUTED,
+            DATA: STATUS_UNEXECUTED
+        }
+
+        self.assertEquals(ZeroTrustService.get_pillars_to_statuses(), expected)
+
+        save_example_findings()
+
+        expected = {
+            AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED,
+            DEVICES: STATUS_FAILED,
+            NETWORKS: STATUS_VERIFY,
+            PEOPLE: STATUS_VERIFY,
+            VISIBILITY_ANALYTICS: STATUS_UNEXECUTED,
+            WORKLOADS: STATUS_UNEXECUTED,
+            DATA: STATUS_FAILED
+        }
+
+        self.assertEquals(ZeroTrustService.get_pillars_to_statuses(), expected)
diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py
new file mode 100644
index 000000000..f4b23f095
--- /dev/null
+++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py
@@ -0,0 +1,150 @@
+import json
+from common.data.zero_trust_consts import *
+from monkey_island.cc.models.zero_trust.finding import Finding
+
+
+class ZeroTrustService(object):
+    @staticmethod
+    def get_pillars_grades():
+        pillars_grades = []
+        for pillar in PILLARS:
+            pillars_grades.append(ZeroTrustService.__get_pillar_grade(pillar))
+        return pillars_grades
+
+    @staticmethod
+    def __get_pillar_grade(pillar):
+        all_findings = Finding.objects()
+        pillar_grade = {
+            "pillar": pillar,
+            STATUS_FAILED: 0,
+            STATUS_VERIFY: 0,
+            STATUS_PASSED: 0,
+            STATUS_UNEXECUTED: 0
+        }
+
+        tests_of_this_pillar = PILLARS_TO_TESTS[pillar]
+
+        test_unexecuted = {}
+        for test in tests_of_this_pillar:
+            test_unexecuted[test] = True
+
+        for finding in all_findings:
+            test_unexecuted[finding.test] = False
+            test_info = TESTS_MAP[finding.test]
+            if pillar in test_info[PILLARS_KEY]:
+                pillar_grade[finding.status] += 1
+
+        pillar_grade[STATUS_UNEXECUTED] = sum(1 for condition in test_unexecuted.values() if condition)
+
+        return pillar_grade
+
+    @staticmethod
+    def get_principles_status():
+        all_principles_statuses = {}
+
+        # init with empty lists
+        for pillar in PILLARS:
+            all_principles_statuses[pillar] = []
+
+        for principle, principle_tests in PRINCIPLES_TO_TESTS.items():
+            for pillar in PRINCIPLES_TO_PILLARS[principle]:
+                all_principles_statuses[pillar].append(
+                    {
+                        "principle": PRINCIPLES[principle],
+                        "tests": ZeroTrustService.__get_tests_status(principle_tests),
+                        "status": ZeroTrustService.__get_principle_status(principle_tests)
+                    }
+                )
+
+        return all_principles_statuses
+
+    @staticmethod
+    def __get_principle_status(principle_tests):
+        worst_status = STATUS_UNEXECUTED
+        all_statuses = set()
+        for test in principle_tests:
+            all_statuses |= set(Finding.objects(test=test).distinct("status"))
+
+        for status in all_statuses:
+            if ORDERED_TEST_STATUSES.index(status) < ORDERED_TEST_STATUSES.index(worst_status):
+                worst_status = status
+
+        return worst_status
+
+    @staticmethod
+    def __get_tests_status(principle_tests):
+        results = []
+        for test in principle_tests:
+            test_findings = Finding.objects(test=test)
+            results.append(
+                {
+                    "test": TESTS_MAP[test][TEST_EXPLANATION_KEY],
+                    "status": ZeroTrustService.__get_lcd_worst_status_for_test(test_findings)
+                }
+            )
+        return results
+
+    @staticmethod
+    def __get_lcd_worst_status_for_test(all_findings_for_test):
+        """
+        :param all_findings_for_test:   All findings of a specific test (get this using Finding.objects(test={A_TEST}))
+        :return:    the "worst" (i.e. most severe) status out of the given findings.
+                    lcd stands for lowest common denominator.
+        """
+        current_worst_status = STATUS_UNEXECUTED
+        for finding in all_findings_for_test:
+            if ORDERED_TEST_STATUSES.index(finding.status) < ORDERED_TEST_STATUSES.index(current_worst_status):
+                current_worst_status = finding.status
+
+        return current_worst_status
+
+    @staticmethod
+    def get_all_findings():
+        all_findings = Finding.objects()
+        enriched_findings = [ZeroTrustService.__get_enriched_finding(f) for f in all_findings]
+        return enriched_findings
+
+    @staticmethod
+    def __get_enriched_finding(finding):
+        test_info = TESTS_MAP[finding.test]
+        enriched_finding = {
+            "test": test_info[FINDING_EXPLANATION_BY_STATUS_KEY][finding.status],
+            "test_key": finding.test,
+            "pillars": test_info[PILLARS_KEY],
+            "status": finding.status,
+            "events": ZeroTrustService.__get_events_as_dict(finding.events)
+        }
+        return enriched_finding
+
+    @staticmethod
+    def __get_events_as_dict(events):
+        return [json.loads(event.to_json()) for event in events]
+
+    @staticmethod
+    def get_statuses_to_pillars():
+        results = {
+            STATUS_FAILED: [],
+            STATUS_VERIFY: [],
+            STATUS_PASSED: [],
+            STATUS_UNEXECUTED: []
+        }
+        for pillar in PILLARS:
+            results[ZeroTrustService.__get_status_of_single_pillar(pillar)].append(pillar)
+
+        return results
+
+    @staticmethod
+    def get_pillars_to_statuses():
+        results = {}
+        for pillar in PILLARS:
+            results[pillar] = ZeroTrustService.__get_status_of_single_pillar(pillar)
+
+        return results
+
+    @staticmethod
+    def __get_status_of_single_pillar(pillar):
+        grade = ZeroTrustService.__get_pillar_grade(pillar)
+        for status in ORDERED_TEST_STATUSES:
+            if grade[status] > 0:
+                return status
+        return STATUS_UNEXECUTED
diff --git a/monkey/monkey_island/cc/services/telemetry/__init__.py b/monkey/monkey_island/cc/services/telemetry/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/__init__.py b/monkey/monkey_island/cc/services/telemetry/processing/__init__.py
new file mode 100644
index 000000000..d90143c09
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/__init__.py
@@ -0,0 +1,7 @@
+# import all implemented hooks, for brevity of hooks.py file
+from tunnel import process_tunnel_telemetry
+from state import process_state_telemetry
+from exploit import process_exploit_telemetry
+from scan import process_scan_telemetry
+from system_info import process_system_info_telemetry
+from post_breach import process_post_breach_telemetry
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py
new file mode 100644
index 000000000..cf6e9b544
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py
@@ -0,0 +1,58 @@
+import copy
+
+import dateutil
+
+from monkey_island.cc.database import mongo
+from monkey_island.cc.encryptor import encryptor
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.services.edge import EdgeService
+from monkey_island.cc.services.node import NodeService
+from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry
+from monkey_island.cc.services.telemetry.zero_trust_tests.machine_exploited import test_machine_exploited
+
+
+def process_exploit_telemetry(telemetry_json):
+    encrypt_exploit_creds(telemetry_json)
+    edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json)
+    update_edge_info_with_new_exploit(edge, telemetry_json)
+    update_node_credentials_from_successful_attempts(edge, telemetry_json)
+
+    test_machine_exploited(
+        current_monkey=Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']),
+        exploit_successful=telemetry_json['data']['result'],
+        exploiter=telemetry_json['data']['exploiter'],
+        target_ip=telemetry_json['data']['machine']['ip_addr'],
+        timestamp=telemetry_json['timestamp'])
+
+
+def update_node_credentials_from_successful_attempts(edge, telemetry_json):
+    for attempt in telemetry_json['data']['attempts']:
+        if attempt['result']:
+            found_creds = {'user': attempt['user']}
+            for field in ['password', 'lm_hash', 'ntlm_hash', 'ssh_key']:
+                if len(attempt[field]) != 0:
+                    found_creds[field] = attempt[field]
+            NodeService.add_credentials_to_node(edge['to'], found_creds)
+
+
+def update_edge_info_with_new_exploit(edge, telemetry_json):
+    telemetry_json['data']['info']['started'] = dateutil.parser.parse(telemetry_json['data']['info']['started'])
+    telemetry_json['data']['info']['finished'] = dateutil.parser.parse(telemetry_json['data']['info']['finished'])
+    new_exploit = copy.deepcopy(telemetry_json['data'])
+    new_exploit.pop('machine')
+    new_exploit['timestamp'] = telemetry_json['timestamp']
+    mongo.db.edge.update(
+        {'_id': edge['_id']},
+        {'$push': {'exploits': new_exploit}}
+    )
+    if new_exploit['result']:
+        EdgeService.set_edge_exploited(edge)
+
+
+def encrypt_exploit_creds(telemetry_json):
+    attempts = telemetry_json['data']['attempts']
+    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] = encryptor.enc(credential.encode('utf-8'))
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
new file mode 100644
index 000000000..c64849905
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
@@ -0,0 +1,27 @@
+from monkey_island.cc.database import mongo
+from common.data.post_breach_consts import *
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import test_new_user_communication
+
+
+def process_communicate_as_new_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]
+    test_new_user_communication(current_monkey, success, message)
+
+
+POST_BREACH_TELEMETRY_PROCESSING_FUNCS = {
+    POST_BREACH_COMMUNICATE_AS_NEW_USER: process_communicate_as_new_user_telemetry,
+}
+
+
+def process_post_breach_telemetry(telemetry_json):
+    mongo.db.monkey.update(
+        {'guid': telemetry_json['monkey_guid']},
+        {'$push': {'pba_results': telemetry_json['data']}})
+
+    post_breach_action_name = telemetry_json["data"]["name"]
+    if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
+        POST_BREACH_TELEMETRY_PROCESSING_FUNCS[post_breach_action_name](telemetry_json)
+
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/processing.py b/monkey/monkey_island/cc/services/telemetry/processing/processing.py
new file mode 100644
index 000000000..154096f79
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/processing.py
@@ -0,0 +1,29 @@
+import logging
+
+from monkey_island.cc.services.telemetry.processing import *
+
+logger = logging.getLogger(__name__)
+
+TELEMETRY_CATEGORY_TO_PROCESSING_FUNC = \
+    {
+        'tunnel': process_tunnel_telemetry,
+        'state': process_state_telemetry,
+        'exploit': process_exploit_telemetry,
+        'scan': process_scan_telemetry,
+        'system_info': process_system_info_telemetry,
+        'post_breach': process_post_breach_telemetry,
+        # `lambda *args, **kwargs: None` is a no-op.
+        'trace': lambda *args, **kwargs: None,
+        'attack': lambda *args, **kwargs: None,
+    }
+
+
+def process_telemetry(telemetry_json):
+    try:
+        telem_category = telemetry_json.get('telem_category')
+        if telem_category in TELEMETRY_CATEGORY_TO_PROCESSING_FUNC:
+            TELEMETRY_CATEGORY_TO_PROCESSING_FUNC[telem_category](telemetry_json)
+        else:
+            logger.info('Got unknown type of telemetry: %s' % telem_category)
+    except Exception as ex:
+        logger.error("Exception caught while processing telemetry. Info: {}".format(ex.message), exc_info=True)
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/scan.py b/monkey/monkey_island/cc/services/telemetry/processing/scan.py
new file mode 100644
index 000000000..bea451170
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/scan.py
@@ -0,0 +1,44 @@
+import copy
+
+from monkey_island.cc.database import mongo
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry
+from monkey_island.cc.services.telemetry.zero_trust_tests.data_endpoints import test_open_data_endpoints
+from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import test_segmentation_violation
+
+
+def process_scan_telemetry(telemetry_json):
+    update_edges_and_nodes_based_on_scan_telemetry(telemetry_json)
+    test_open_data_endpoints(telemetry_json)
+
+    current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
+    target_ip = telemetry_json['data']['machine']['ip_addr']
+    test_segmentation_violation(current_monkey, target_ip)
+
+
+def update_edges_and_nodes_based_on_scan_telemetry(telemetry_json):
+    edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json)
+    data = copy.deepcopy(telemetry_json['data']['machine'])
+    ip_address = data.pop("ip_addr")
+    domain_name = data.pop("domain_name")
+    new_scan = \
+        {
+            "timestamp": telemetry_json["timestamp"],
+            "data": data
+        }
+    mongo.db.edge.update(
+        {"_id": edge["_id"]},
+        {"$push": {"scans": new_scan},
+         "$set": {"ip_address": ip_address, 'domain_name': domain_name}}
+    )
+    node = mongo.db.node.find_one({"_id": edge["to"]})
+    if node is not None:
+        scan_os = new_scan["data"]["os"]
+        if "type" in scan_os:
+            mongo.db.node.update({"_id": node["_id"]},
+                                 {"$set": {"os.type": scan_os["type"]}},
+                                 upsert=False)
+        if "version" in scan_os:
+            mongo.db.node.update({"_id": node["_id"]},
+                                 {"$set": {"os.version": scan_os["version"]}},
+                                 upsert=False)
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/state.py b/monkey/monkey_island/cc/services/telemetry/processing/state.py
new file mode 100644
index 000000000..4e164e900
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/state.py
@@ -0,0 +1,17 @@
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.services.node import NodeService
+from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import \
+    test_passed_findings_for_unreached_segments
+
+
+def process_state_telemetry(telemetry_json):
+    monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
+    NodeService.add_communication_info(monkey, telemetry_json['command_control_channel'])
+    if telemetry_json['data']['done']:
+        NodeService.set_monkey_dead(monkey, True)
+    else:
+        NodeService.set_monkey_dead(monkey, False)
+
+    if telemetry_json['data']['done']:
+        current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
+        test_passed_findings_for_unreached_segments(current_monkey)
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py
new file mode 100644
index 000000000..ebf11c219
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py
@@ -0,0 +1,99 @@
+from monkey_island.cc.database import mongo
+from monkey_island.cc.services import mimikatz_utils
+from monkey_island.cc.services.node import NodeService
+from monkey_island.cc.services.config import ConfigService
+from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence
+from monkey_island.cc.services.wmi_handler import WMIHandler
+from monkey_island.cc.encryptor import encryptor
+
+
+def process_system_info_telemetry(telemetry_json):
+    process_ssh_info(telemetry_json)
+    process_credential_info(telemetry_json)
+    process_mimikatz_and_wmi_info(telemetry_json)
+    process_aws_data(telemetry_json)
+    test_antivirus_existence(telemetry_json)
+
+
+def process_ssh_info(telemetry_json):
+    if 'ssh_info' in telemetry_json['data']:
+        ssh_info = telemetry_json['data']['ssh_info']
+        encrypt_system_info_ssh_keys(ssh_info)
+        if telemetry_json['data']['network_info']['networks']:
+            # We use user_name@machine_ip as the name of the ssh key stolen, thats why we need ip from telemetry
+            add_ip_to_ssh_keys(telemetry_json['data']['network_info']['networks'][0], ssh_info)
+        add_system_info_ssh_keys_to_config(ssh_info)
+
+
+def add_system_info_ssh_keys_to_config(ssh_info):
+    for user in ssh_info:
+        ConfigService.creds_add_username(user['name'])
+        # Public key is useless without private key
+        if user['public_key'] and user['private_key']:
+            ConfigService.ssh_add_keys(user['public_key'], user['private_key'],
+                                       user['name'], user['ip'])
+
+
+def add_ip_to_ssh_keys(ip, ssh_info):
+    for key in ssh_info:
+        key['ip'] = ip['addr']
+
+
+def encrypt_system_info_ssh_keys(ssh_info):
+    for idx, user in enumerate(ssh_info):
+        for field in ['public_key', 'private_key', 'known_hosts']:
+            if ssh_info[idx][field]:
+                ssh_info[idx][field] = encryptor.enc(ssh_info[idx][field].encode('utf-8'))
+
+
+def process_credential_info(telemetry_json):
+    if 'credentials' in telemetry_json['data']:
+        creds = telemetry_json['data']['credentials']
+        encrypt_system_info_creds(creds)
+        add_system_info_creds_to_config(creds)
+        replace_user_dot_with_comma(creds)
+
+
+def replace_user_dot_with_comma(creds):
+    for user in creds:
+        if -1 != user.find('.'):
+            new_user = user.replace('.', ',')
+            creds[new_user] = creds.pop(user)
+
+
+def add_system_info_creds_to_config(creds):
+    for user in creds:
+        ConfigService.creds_add_username(user)
+        if 'password' in creds[user]:
+            ConfigService.creds_add_password(creds[user]['password'])
+        if 'lm_hash' in creds[user]:
+            ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
+        if 'ntlm_hash' in creds[user]:
+            ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
+
+
+def encrypt_system_info_creds(creds):
+    for user in creds:
+        for field in ['password', 'lm_hash', 'ntlm_hash']:
+            if field in creds[user]:
+                # this encoding is because we might run into passwords which are not pure ASCII
+                creds[user][field] = encryptor.enc(creds[user][field].encode('utf-8'))
+
+
+def process_mimikatz_and_wmi_info(telemetry_json):
+    users_secrets = {}
+    if 'mimikatz' in telemetry_json['data']:
+        users_secrets = mimikatz_utils.MimikatzSecrets. \
+            extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', ''))
+    if 'wmi' in telemetry_json['data']:
+        monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id')
+        wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)
+        wmi_handler.process_and_handle_wmi_info()
+
+
+def process_aws_data(telemetry_json):
+    if 'aws' in telemetry_json['data']:
+        if 'instance_id' in telemetry_json['data']['aws']:
+            monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id')
+            mongo.db.monkey.update_one({'_id': monkey_id},
+                                       {'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}})
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py b/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py
new file mode 100644
index 000000000..1598b144a
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py
@@ -0,0 +1,13 @@
+from monkey_island.cc.services.node import NodeService
+from monkey_island.cc.services.telemetry.processing.utils import get_tunnel_host_ip_from_proxy_field
+from monkey_island.cc.services.telemetry.zero_trust_tests.tunneling import test_tunneling_violation
+
+
+def process_tunnel_telemetry(telemetry_json):
+    test_tunneling_violation(telemetry_json)
+    monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"]
+    if telemetry_json['data']['proxy'] is not None:
+        tunnel_host_ip = get_tunnel_host_ip_from_proxy_field(telemetry_json)
+        NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip)
+    else:
+        NodeService.unset_all_monkey_tunnels(monkey_id)
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/utils.py b/monkey/monkey_island/cc/services/telemetry/processing/utils.py
new file mode 100644
index 000000000..466b81bf1
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/processing/utils.py
@@ -0,0 +1,18 @@
+from monkey_island.cc.services.edge import EdgeService
+from monkey_island.cc.services.node import NodeService
+
+
+def get_edge_by_scan_or_exploit_telemetry(telemetry_json):
+    dst_ip = telemetry_json['data']['machine']['ip_addr']
+    dst_domain_name = telemetry_json['data']['machine']['domain_name']
+    src_monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
+    dst_node = NodeService.get_monkey_by_ip(dst_ip)
+    if dst_node is None:
+        dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name)
+
+    return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"])
+
+
+def get_tunnel_host_ip_from_proxy_field(telemetry_json):
+    tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "")
+    return tunnel_host_ip
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/__init__.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py
new file mode 100644
index 000000000..b8b8c559b
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py
@@ -0,0 +1,47 @@
+import json
+
+from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, \
+    STATUS_PASSED, STATUS_FAILED, TEST_ENDPOINT_SECURITY_EXISTS
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.services.telemetry.zero_trust_tests.known_anti_viruses import ANTI_VIRUS_KNOWN_PROCESS_NAMES
+
+
+def test_antivirus_existence(telemetry_json):
+    current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
+    if 'process_list' in telemetry_json['data']:
+        process_list_event = Event.create_event(
+            title="Process list",
+            message="Monkey on {} scanned the process list".format(current_monkey.hostname),
+            event_type=EVENT_TYPE_MONKEY_LOCAL)
+        events = [process_list_event]
+
+        av_processes = filter_av_processes(telemetry_json)
+
+        for process in av_processes:
+            events.append(Event.create_event(
+                title="Found AV process",
+                message="The process '{}' was recognized as an Anti Virus process. Process "
+                        "details: {}".format(process[1]['name'], json.dumps(process[1])),
+                event_type=EVENT_TYPE_MONKEY_LOCAL
+            ))
+
+        if len(av_processes) > 0:
+            test_status = STATUS_PASSED
+        else:
+            test_status = STATUS_FAILED
+        AggregateFinding.create_or_add_to_existing(
+            test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events
+        )
+
+
+def filter_av_processes(telemetry_json):
+    all_processes = telemetry_json['data']['process_list'].items()
+    av_processes = []
+    for process in all_processes:
+        process_name = process[1]['name']
+        # This is for case-insensitive `in`. Generator expression is to save memory.
+        if process_name.upper() in (known_av_name.upper() for known_av_name in ANTI_VIRUS_KNOWN_PROCESS_NAMES):
+            av_processes.append(process)
+    return av_processes
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
new file mode 100644
index 000000000..6c5b1154b
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
@@ -0,0 +1,37 @@
+from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK, STATUS_FAILED, TEST_COMMUNICATE_AS_NEW_USER, \
+    STATUS_PASSED
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.event import Event
+
+COMM_AS_NEW_USER_FAILED_FORMAT = "Monkey on {} couldn't communicate as new user. Details: {}"
+COMM_AS_NEW_USER_SUCCEEDED_FORMAT = \
+    "New user created by Monkey on {} successfully tried to communicate with the internet. Details: {}"
+
+
+def test_new_user_communication(current_monkey, success, message):
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_COMMUNICATE_AS_NEW_USER,
+        # If the monkey succeeded to create a user, then the test failed.
+        status=STATUS_FAILED if success else STATUS_PASSED,
+        events=[
+            get_attempt_event(current_monkey),
+            get_result_event(current_monkey, message, success)
+        ]
+    )
+
+
+def get_attempt_event(current_monkey):
+    tried_to_communicate_event = Event.create_event(
+        title="Communicate as new user",
+        message="Monkey on {} tried to create a new user and communicate from it.".format(current_monkey.hostname),
+        event_type=EVENT_TYPE_MONKEY_NETWORK)
+    return tried_to_communicate_event
+
+
+def get_result_event(current_monkey, message, success):
+    message_format = COMM_AS_NEW_USER_SUCCEEDED_FORMAT if success else COMM_AS_NEW_USER_FAILED_FORMAT
+
+    return Event.create_event(
+        title="Communicate as new user",
+        message=message_format.format(current_monkey.hostname, message),
+        event_type=EVENT_TYPE_MONKEY_NETWORK)
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
new file mode 100644
index 000000000..68a7f713d
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
@@ -0,0 +1,70 @@
+import json
+
+from common.data.network_consts import ES_SERVICE
+from common.data.zero_trust_consts import *
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline
+from monkey_island.cc.models.zero_trust.event import Event
+
+HTTP_SERVERS_SERVICES_NAMES = ['tcp-80']
+
+
+def test_open_data_endpoints(telemetry_json):
+    services = telemetry_json["data"]["machine"]["services"]
+    current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
+    found_http_server_status = STATUS_PASSED
+    found_elastic_search_server = STATUS_PASSED
+
+    events = [
+        Event.create_event(
+            title="Scan Telemetry",
+            message="Monkey on {} tried to perform a network scan, the target was {}.".format(
+                current_monkey.hostname,
+                telemetry_json["data"]["machine"]["ip_addr"]),
+            event_type=EVENT_TYPE_MONKEY_NETWORK,
+            timestamp=telemetry_json["timestamp"]
+        )
+    ]
+
+    for service_name, service_data in services.items():
+        events.append(Event.create_event(
+            title="Scan telemetry analysis",
+            message="Scanned service: {}.".format(service_name),
+            event_type=EVENT_TYPE_MONKEY_NETWORK
+        ))
+        if service_name in HTTP_SERVERS_SERVICES_NAMES:
+            found_http_server_status = STATUS_FAILED
+            events.append(Event.create_event(
+                title="Scan telemetry analysis",
+                message="Service {} on {} recognized as an open data endpoint! Service details: {}".format(
+                    service_data["display_name"],
+                    telemetry_json["data"]["machine"]["ip_addr"],
+                    json.dumps(service_data)
+                ),
+                event_type=EVENT_TYPE_MONKEY_NETWORK
+            ))
+        if service_name == ES_SERVICE:
+            found_elastic_search_server = STATUS_FAILED
+            events.append(Event.create_event(
+                title="Scan telemetry analysis",
+                message="Service {} on {} recognized as an open data endpoint! Service details: {}".format(
+                    service_data["display_name"],
+                    telemetry_json["data"]["machine"]["ip_addr"],
+                    json.dumps(service_data)
+                ),
+                event_type=EVENT_TYPE_MONKEY_NETWORK
+            ))
+
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_DATA_ENDPOINT_HTTP,
+        status=found_http_server_status,
+        events=events
+    )
+
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_DATA_ENDPOINT_ELASTIC,
+        status=found_elastic_search_server,
+        events=events
+    )
+
+    add_malicious_activity_to_timeline(events)
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py
new file mode 100644
index 000000000..e5d7c2355
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py
@@ -0,0 +1,87 @@
+ANTI_VIRUS_KNOWN_PROCESS_NAMES = [
+    u"AvastSvc.exe",
+    u"AvastUI.exe",
+    u"avcenter.exe",
+    u"avconfig.exe",
+    u"avgcsrvx.exe",
+    u"avgidsagent.exe",
+    u"avgnt.exe",
+    u"avgrsx.exe",
+    u"avguard.exe",
+    u"avgui.exe",
+    u"avgwdsvc.exe",
+    u"avp.exe",
+    u"avscan.exe",
+    u"bdagent.exe",
+    u"ccuac.exe",
+    u"egui.exe",
+    u"hijackthis.exe",
+    u"instup.exe",
+    u"keyscrambler.exe",
+    u"mbam.exe",
+    u"mbamgui.exe",
+    u"mbampt.exe",
+    u"mbamscheduler.exe",
+    u"mbamservice.exe",
+    u"MpCmdRun.exe",
+    u"MSASCui.exe",
+    u"MsMpEng.exe",
+    u"rstrui.exe",
+    u"spybotsd.exe",
+    u"zlclient.exe",
+    u"SymCorpUI.exe",
+    u"ccSvcHst.exe",
+    u"ccApp.exe",
+    u"LUALL.exe",
+    u"SMC.exe",
+    u"SMCgui.exe",
+    u"Rtvscan.exe",
+    u"LuComServer.exe",
+    u"ProtectionUtilSurrogate.exe",
+    u"ClientRemote.exe",
+    u"SemSvc.exe",
+    u"SemLaunchSvc.exe",
+    u"sesmcontinst.exe",
+    u"LuCatalog.exe",
+    u"LUALL.exe",
+    u"LuCallbackProxy.exe",
+    u"LuComServer_3_3.exe",
+    u"httpd.exe",
+    u"dbisqlc.exe",
+    u"dbsrv16.exe",
+    u"semapisrv.exe",
+    u"snac64.exe",
+    u"AutoExcl.exe",
+    u"DoScan.exe",
+    u"nlnhook.exe",
+    u"SavUI.exe",
+    u"SepLiveUpdate.exe",
+    u"Smc.exe",
+    u"SmcGui.exe",
+    u"SymCorpUI.exe",
+    u"symerr.exe",
+    u"ccSvcHst.exe",
+    u"DevViewer.exe",
+    u"DWHWizrd.exe",
+    u"RtvStart.exe",
+    u"roru.exe",
+    u"WSCSAvNotifier",
+    # Guardicore Centra
+    # Linux
+    u"gc-agents-service",
+    u"gc-guest-agent",
+    u"gc-guardig",
+    u"gc-digger",
+    u"gc-fastpath",
+    u"gc-enforcement-agent",
+    u"gc-enforcement-channel",
+    u"gc-detection-agent",
+    # Windows
+    u"gc-guest-agent.exe",
+    u"gc-windig.exe",
+    u"gc-digger.exe",
+    u"gc-fastpath.exe",
+    u"gc-enforcement-channel.exe",
+    u"gc-enforcement-agent.exe",
+    u"gc-agent-ui.exe"
+]
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
new file mode 100644
index 000000000..454f3a7fe
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
@@ -0,0 +1,39 @@
+from common.data.zero_trust_consts import *
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline
+from monkey_island.cc.models.zero_trust.event import Event
+
+
+def test_machine_exploited(current_monkey, exploit_successful, exploiter, target_ip, timestamp):
+    events = [
+        Event.create_event(
+            title="Exploit attempt",
+            message="Monkey on {} attempted to exploit {} using {}.".format(
+                current_monkey.hostname,
+                target_ip,
+                exploiter),
+            event_type=EVENT_TYPE_MONKEY_NETWORK,
+            timestamp=timestamp
+        )
+    ]
+    status = STATUS_PASSED
+    if exploit_successful:
+        events.append(
+            Event.create_event(
+                title="Exploit success!",
+                message="Monkey on {} successfully exploited {} using {}.".format(
+                    current_monkey.hostname,
+                    target_ip,
+                    exploiter),
+                event_type=EVENT_TYPE_MONKEY_NETWORK,
+                timestamp=timestamp)
+        )
+        status = STATUS_FAILED
+
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_MACHINE_EXPLOITED,
+        status=status,
+        events=events
+    )
+
+    add_malicious_activity_to_timeline(events)
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py
new file mode 100644
index 000000000..50e60e493
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py
@@ -0,0 +1,110 @@
+import itertools
+from six import text_type
+
+from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_PASSED
+from common.network.network_range import NetworkRange
+from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst, get_ip_if_in_subnet
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding
+from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups
+
+SEGMENTATION_DONE_EVENT_TEXT = "Monkey on {hostname} is done attempting cross-segment communications " \
+                       "from `{src_seg}` segments to `{dst_seg}` segments."
+
+SEGMENTATION_VIOLATION_EVENT_TEXT = \
+    "Segmentation violation! Monkey on '{hostname}', with the {source_ip} IP address (in segment {source_seg}) " \
+    "managed to communicate cross segment to {target_ip} (in segment {target_seg})."
+
+
+def test_segmentation_violation(current_monkey, target_ip):
+    # TODO - lower code duplication between this and report.py.
+    subnet_groups = get_config_network_segments_as_subnet_groups()
+    for subnet_group in subnet_groups:
+        subnet_pairs = itertools.product(subnet_group, subnet_group)
+        for subnet_pair in subnet_pairs:
+            source_subnet = subnet_pair[0]
+            target_subnet = subnet_pair[1]
+            if is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet):
+                event = get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet)
+                SegmentationFinding.create_or_add_to_existing_finding(
+                    subnets=[source_subnet, target_subnet],
+                    status=STATUS_FAILED,
+                    segmentation_event=event
+                )
+
+
+def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet):
+    # type: (Monkey, str, str, str) -> bool
+    """
+    Checks is a specific communication is a segmentation violation.
+    :param current_monkey:  The source monkey which originated the communication.
+    :param target_ip:       The target with which the current monkey communicated with.
+    :param source_subnet:   The segment the monkey belongs to.
+    :param target_subnet:   Another segment which the monkey isn't supposed to communicate with.
+    :return:    True if this is a violation of segmentation between source_subnet and target_subnet; Otherwise, False.
+    """
+    if source_subnet == target_subnet:
+        return False
+    source_subnet_range = NetworkRange.get_range_obj(source_subnet)
+    target_subnet_range = NetworkRange.get_range_obj(target_subnet)
+
+    if target_subnet_range.is_in_range(text_type(target_ip)):
+        cross_segment_ip = get_ip_in_src_and_not_in_dst(
+            current_monkey.ip_addresses,
+            source_subnet_range,
+            target_subnet_range)
+
+        return cross_segment_ip is not None
+
+
+def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet):
+    return Event.create_event(
+        title="Segmentation event",
+        message=SEGMENTATION_VIOLATION_EVENT_TEXT.format(
+            hostname=current_monkey.hostname,
+            source_ip=get_ip_if_in_subnet(current_monkey.ip_addresses, NetworkRange.get_range_obj(source_subnet)),
+            source_seg=source_subnet,
+            target_ip=target_ip,
+            target_seg=target_subnet
+        ),
+        event_type=EVENT_TYPE_MONKEY_NETWORK
+    )
+
+
+def test_passed_findings_for_unreached_segments(current_monkey):
+    flat_all_subnets = [item for sublist in get_config_network_segments_as_subnet_groups() for item in sublist]
+    create_or_add_findings_for_all_pairs(flat_all_subnets, current_monkey)
+
+
+def create_or_add_findings_for_all_pairs(all_subnets, current_monkey):
+    # Filter the subnets that this monkey is part of.
+    this_monkey_subnets = []
+    for subnet in all_subnets:
+        if get_ip_if_in_subnet(current_monkey.ip_addresses, NetworkRange.get_range_obj(subnet)) is not None:
+            this_monkey_subnets.append(subnet)
+
+    # Get all the other subnets.
+    other_subnets = list(set(all_subnets) - set(this_monkey_subnets))
+
+    # Calculate the cartesian product - (this monkey subnets X other subnets). These pairs are the pairs that the monkey
+    # should have tested.
+    all_subnets_pairs_for_this_monkey = itertools.product(this_monkey_subnets, other_subnets)
+
+    for subnet_pair in all_subnets_pairs_for_this_monkey:
+        SegmentationFinding.create_or_add_to_existing_finding(
+            subnets=list(subnet_pair),
+            status=STATUS_PASSED,
+            segmentation_event=get_segmentation_done_event(current_monkey, subnet_pair)
+        )
+
+
+def get_segmentation_done_event(current_monkey, subnet_pair):
+    return Event.create_event(
+        title="Segmentation test done",
+        message=SEGMENTATION_DONE_EVENT_TEXT.format(
+                    hostname=current_monkey.hostname,
+                    src_seg=subnet_pair[0],
+                    dst_seg=subnet_pair[1]),
+        event_type=EVENT_TYPE_MONKEY_NETWORK
+    )
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py
new file mode 100644
index 000000000..5f986e3b5
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py
@@ -0,0 +1,46 @@
+import uuid
+
+from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_PASSED, STATUS_FAILED, \
+    EVENT_TYPE_MONKEY_NETWORK
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.models.zero_trust.finding import Finding
+from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding
+from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import create_or_add_findings_for_all_pairs
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+
+FIRST_SUBNET = "1.1.1.1"
+SECOND_SUBNET = "2.2.2.0/24"
+THIRD_SUBNET = "3.3.3.3-3.3.3.200"
+
+
+class TestSegmentationTests(IslandTestCase):
+    def test_create_findings_for_all_done_pairs(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        all_subnets = [FIRST_SUBNET, SECOND_SUBNET, THIRD_SUBNET]
+
+        monkey = Monkey(
+            guid=str(uuid.uuid4()),
+            ip_addresses=[FIRST_SUBNET])
+
+        # no findings
+        self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 0)
+
+        # This is like the monkey is done and sent done telem
+        create_or_add_findings_for_all_pairs(all_subnets, monkey)
+
+        # There are 2 subnets in which the monkey is NOT
+        self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_PASSED)), 2)
+
+        # This is a monkey from 2nd subnet communicated with 1st subnet.
+        SegmentationFinding.create_or_add_to_existing_finding(
+            [FIRST_SUBNET, SECOND_SUBNET],
+            STATUS_FAILED,
+            Event.create_event(title="sdf", message="asd", event_type=EVENT_TYPE_MONKEY_NETWORK)
+        )
+
+        self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_PASSED)), 1)
+        self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_FAILED)), 1)
+        self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 2)
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
new file mode 100644
index 000000000..ce34c2bb4
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
@@ -0,0 +1,27 @@
+from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.services.telemetry.processing.utils import get_tunnel_host_ip_from_proxy_field
+
+
+def test_tunneling_violation(tunnel_telemetry_json):
+    if tunnel_telemetry_json['data']['proxy'] is not None:
+        # Monkey is tunneling, create findings
+        tunnel_host_ip = get_tunnel_host_ip_from_proxy_field(tunnel_telemetry_json)
+        current_monkey = Monkey.get_single_monkey_by_guid(tunnel_telemetry_json['monkey_guid'])
+        tunneling_events = [Event.create_event(
+            title="Tunneling event",
+            message="Monkey on {hostname} tunneled traffic through {proxy}.".format(
+                hostname=current_monkey.hostname, proxy=tunnel_host_ip),
+            event_type=EVENT_TYPE_MONKEY_NETWORK,
+            timestamp=tunnel_telemetry_json['timestamp']
+        )]
+
+        AggregateFinding.create_or_add_to_existing(
+            test=TEST_TUNNELING,
+            status=STATUS_FAILED,
+            events=tunneling_events
+        )
+
+        add_malicious_activity_to_timeline(tunneling_events)
diff --git a/monkey/monkey_island/cc/testing/IslandTestCase.py b/monkey/monkey_island/cc/testing/IslandTestCase.py
index e894f13df..6bca20f4a 100644
--- a/monkey/monkey_island/cc/testing/IslandTestCase.py
+++ b/monkey/monkey_island/cc/testing/IslandTestCase.py
@@ -1,6 +1,7 @@
 import unittest
 from monkey_island.cc.environment.environment import env
 from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.finding import Finding
 
 
 class IslandTestCase(unittest.TestCase):
@@ -10,3 +11,7 @@ class IslandTestCase(unittest.TestCase):
     @staticmethod
     def clean_monkey_db():
         Monkey.objects().delete()
+
+    @staticmethod
+    def clean_finding_db():
+        Finding.objects().delete()
diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json
index 934b567e7..f366d73bd 100644
--- a/monkey/monkey_island/cc/ui/package-lock.json
+++ b/monkey/monkey_island/cc/ui/package-lock.json
@@ -56,7 +56,7 @@
     "@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": "sha1-lggbcRHkhtpNLNlxrRpP4hbMLj0=",
+      "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
       "requires": {
         "@babel/types": "^7.0.0"
       },
@@ -74,7 +74,7 @@
         "lodash": {
           "version": "4.17.11",
           "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
-          "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40="
+          "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
         },
         "to-fast-properties": {
           "version": "2.0.0",
@@ -259,7 +259,7 @@
     "@emotion/cache": {
       "version": "10.0.9",
       "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.9.tgz",
-      "integrity": "sha1-4Me3oon3Uw7c+tTc84WL0uVwCm8=",
+      "integrity": "sha512-f7MblpE2xoimC4fCMZ9pivmsIn7hyWRIvY75owMDi8pdOSeh+w5tH3r4hBJv/LLrwiMM7cTQURqTPcYoL5pWnw==",
       "requires": {
         "@emotion/sheet": "0.9.2",
         "@emotion/stylis": "0.8.3",
@@ -270,7 +270,7 @@
     "@emotion/core": {
       "version": "10.0.10",
       "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.10.tgz",
-      "integrity": "sha1-jTEU5aL4sXinBnxgOik3UW8YCwg=",
+      "integrity": "sha512-U1aE2cOWUscjc8ZJ3Cx32udOzLeRoJwGxBH93xQD850oQFpwPKZARzdUtdc9SByUOwzSFYxhDhrpXnV34FJmWg==",
       "requires": {
         "@emotion/cache": "^10.0.9",
         "@emotion/css": "^10.0.9",
@@ -292,12 +292,12 @@
     "@emotion/hash": {
       "version": "0.7.1",
       "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.1.tgz",
-      "integrity": "sha1-mDNyI0E3n7fWfwaksAqzw3kT2lM="
+      "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": "sha1-6TwTlCWSz17wGqgpdETcGSvu5S8="
+      "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg=="
     },
     "@emotion/serialize": {
       "version": "0.11.6",
@@ -314,27 +314,27 @@
     "@emotion/sheet": {
       "version": "0.9.2",
       "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.2.tgz",
-      "integrity": "sha1-dOXGteSJobowqyRqte7dlpFkh8Q="
+      "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": "sha1-PKfpvLMbPLSvuutmFW2G7oXiMkY="
+      "integrity": "sha512-M3nMfJ6ndJMYloSIbYEBq6G3eqoYD41BpDOxreE8j0cb4fzz/5qvmqU9Mb2hzsXcCnIlGlWhS03PCzVGvTAe0Q=="
     },
     "@emotion/unitless": {
       "version": "0.7.3",
       "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz",
-      "integrity": "sha1-YxCgR/EtIaEDb7AxMXIZiSRAQW8="
+      "integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg=="
     },
     "@emotion/utils": {
       "version": "0.11.1",
       "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.1.tgz",
-      "integrity": "sha1-hSm3QSputLSL325yDMG45uHhdig="
+      "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": "sha1-Y5hdPYsCUw4IaZYvTaCRQu6OIA4="
+      "integrity": "sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA=="
     },
     "@kunukn/react-collapse": {
       "version": "1.0.5",
@@ -630,7 +630,6 @@
       "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
       "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
       "dev": true,
-      "optional": true,
       "requires": {
         "kind-of": "^3.0.2",
         "longest": "^1.0.1",
@@ -689,7 +688,7 @@
     "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": "sha1-SzXClE8GKov82mZBB2A1D+nd/CE=",
+      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
       "requires": {
         "delegates": "^1.0.0",
         "readable-stream": "^2.0.6"
@@ -703,12 +702,12 @@
         "process-nextick-args": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o="
+          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
         },
         "readable-stream": {
           "version": "2.3.6",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
+          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "requires": {
             "core-util-is": "~1.0.0",
             "inherits": "~2.0.3",
@@ -722,7 +721,7 @@
         "string_decoder": {
           "version": "1.1.1",
           "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "requires": {
             "safe-buffer": "~5.1.0"
           }
@@ -3004,7 +3003,7 @@
     "clone-deep": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz",
-      "integrity": "sha1-ANs6Hhc2VnMNEYjD1qztbX6pdxM=",
+      "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==",
       "requires": {
         "for-own": "^1.0.0",
         "is-plain-object": "^2.0.4",
@@ -3023,7 +3022,7 @@
         "kind-of": {
           "version": "6.0.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE="
+          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
         }
       }
     },
@@ -3088,8 +3087,7 @@
     "commander": {
       "version": "2.15.1",
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
-      "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
-      "dev": true
+      "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
     },
     "commondir": {
       "version": "1.0.1",
@@ -3313,7 +3311,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"
       }
@@ -3369,7 +3367,7 @@
         },
         "yargs": {
           "version": "11.1.0",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
+          "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
           "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
           "dev": true,
           "requires": {
@@ -3420,8 +3418,8 @@
           "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
           "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
           "requires": {
-            "argparse": "1.0.9",
-            "esprima": "4.0.1"
+            "argparse": "^1.0.7",
+            "esprima": "^4.0.0"
           }
         },
         "parse-json": {
@@ -3429,8 +3427,8 @@
           "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
           "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
           "requires": {
-            "error-ex": "1.3.2",
-            "json-parse-better-errors": "1.0.2"
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1"
           }
         }
       }
@@ -3658,6 +3656,270 @@
         "es5-ext": "^0.10.9"
       }
     },
+    "d3": {
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/d3/-/d3-5.11.0.tgz",
+      "integrity": "sha512-LXgMVUAEAzQh6WfEEOa8tJX4RA64ZJ6twC3CJ+Xzid+fXWLTZkkglagXav/eOoQgzQi5rzV0xC4Sfspd6hFDHA==",
+      "requires": {
+        "d3-array": "1",
+        "d3-axis": "1",
+        "d3-brush": "1",
+        "d3-chord": "1",
+        "d3-collection": "1",
+        "d3-color": "1",
+        "d3-contour": "1",
+        "d3-dispatch": "1",
+        "d3-drag": "1",
+        "d3-dsv": "1",
+        "d3-ease": "1",
+        "d3-fetch": "1",
+        "d3-force": "1",
+        "d3-format": "1",
+        "d3-geo": "1",
+        "d3-hierarchy": "1",
+        "d3-interpolate": "1",
+        "d3-path": "1",
+        "d3-polygon": "1",
+        "d3-quadtree": "1",
+        "d3-random": "1",
+        "d3-scale": "2",
+        "d3-scale-chromatic": "1",
+        "d3-selection": "1",
+        "d3-shape": "1",
+        "d3-time": "1",
+        "d3-time-format": "2",
+        "d3-timer": "1",
+        "d3-transition": "1",
+        "d3-voronoi": "1",
+        "d3-zoom": "1"
+      }
+    },
+    "d3-array": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+      "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+    },
+    "d3-axis": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
+      "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
+    },
+    "d3-brush": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.3.tgz",
+      "integrity": "sha512-v8bbYyCFKjyCzFk/tdWqXwDykY8YWqhXYjcYxfILIit085VZOpj4XJKOMccTsvWxgzSLMJQg5SiqHjslsipEDg==",
+      "requires": {
+        "d3-dispatch": "1",
+        "d3-drag": "1",
+        "d3-interpolate": "1",
+        "d3-selection": "1",
+        "d3-transition": "1"
+      }
+    },
+    "d3-chord": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
+      "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
+      "requires": {
+        "d3-array": "1",
+        "d3-path": "1"
+      }
+    },
+    "d3-collection": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
+      "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
+    },
+    "d3-color": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.3.0.tgz",
+      "integrity": "sha512-NHODMBlj59xPAwl2BDiO2Mog6V+PrGRtBfWKqKRrs9MCqlSkIEb0Z/SfY7jW29ReHTDC/j+vwXhnZcXI3+3fbg=="
+    },
+    "d3-contour": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz",
+      "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==",
+      "requires": {
+        "d3-array": "^1.1.1"
+      }
+    },
+    "d3-dispatch": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz",
+      "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g=="
+    },
+    "d3-drag": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.4.tgz",
+      "integrity": "sha512-ICPurDETFAelF1CTHdIyiUM4PsyZLaM+7oIBhmyP+cuVjze5vDZ8V//LdOFjg0jGnFIZD/Sfmk0r95PSiu78rw==",
+      "requires": {
+        "d3-dispatch": "1",
+        "d3-selection": "1"
+      }
+    },
+    "d3-dsv": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.1.1.tgz",
+      "integrity": "sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==",
+      "requires": {
+        "commander": "2",
+        "iconv-lite": "0.4",
+        "rw": "1"
+      }
+    },
+    "d3-ease": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz",
+      "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ=="
+    },
+    "d3-fetch": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz",
+      "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==",
+      "requires": {
+        "d3-dsv": "1"
+      }
+    },
+    "d3-force": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz",
+      "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==",
+      "requires": {
+        "d3-collection": "1",
+        "d3-dispatch": "1",
+        "d3-quadtree": "1",
+        "d3-timer": "1"
+      }
+    },
+    "d3-format": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz",
+      "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ=="
+    },
+    "d3-geo": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.6.tgz",
+      "integrity": "sha512-z0J8InXR9e9wcgNtmVnPTj0TU8nhYT6lD/ak9may2PdKqXIeHUr8UbFLoCtrPYNsjv6YaLvSDQVl578k6nm7GA==",
+      "requires": {
+        "d3-array": "1"
+      }
+    },
+    "d3-hierarchy": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz",
+      "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w=="
+    },
+    "d3-interpolate": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz",
+      "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==",
+      "requires": {
+        "d3-color": "1"
+      }
+    },
+    "d3-path": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.8.tgz",
+      "integrity": "sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg=="
+    },
+    "d3-polygon": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz",
+      "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w=="
+    },
+    "d3-quadtree": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz",
+      "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA=="
+    },
+    "d3-random": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
+      "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
+    },
+    "d3-scale": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
+      "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
+      "requires": {
+        "d3-array": "^1.2.0",
+        "d3-collection": "1",
+        "d3-format": "1",
+        "d3-interpolate": "1",
+        "d3-time": "1",
+        "d3-time-format": "2"
+      }
+    },
+    "d3-scale-chromatic": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz",
+      "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==",
+      "requires": {
+        "d3-color": "1",
+        "d3-interpolate": "1"
+      }
+    },
+    "d3-selection": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz",
+      "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg=="
+    },
+    "d3-shape": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz",
+      "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==",
+      "requires": {
+        "d3-path": "1"
+      }
+    },
+    "d3-time": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz",
+      "integrity": "sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw=="
+    },
+    "d3-time-format": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz",
+      "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==",
+      "requires": {
+        "d3-time": "1"
+      }
+    },
+    "d3-timer": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz",
+      "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg=="
+    },
+    "d3-transition": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.2.0.tgz",
+      "integrity": "sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==",
+      "requires": {
+        "d3-color": "1",
+        "d3-dispatch": "1",
+        "d3-ease": "1",
+        "d3-interpolate": "1",
+        "d3-selection": "^1.1.0",
+        "d3-timer": "1"
+      }
+    },
+    "d3-voronoi": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
+      "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
+    },
+    "d3-zoom": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz",
+      "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==",
+      "requires": {
+        "d3-dispatch": "1",
+        "d3-drag": "1",
+        "d3-interpolate": "1",
+        "d3-selection": "1",
+        "d3-transition": "1"
+      }
+    },
     "dashdash": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -5199,6 +5461,11 @@
         }
       }
     },
+    "file-saver": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz",
+      "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw=="
+    },
     "filename-regex": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
@@ -5271,7 +5538,7 @@
     "find-root": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
-      "integrity": "sha1-q8/Iunb3CMQql7PWhbfpRQv7nOQ="
+      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
     },
     "find-up": {
       "version": "1.1.2",
@@ -5523,15 +5790,14 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "co": "4.6.0",
-            "json-stable-stringify": "1.0.1"
+            "co": "^4.6.0",
+            "json-stable-stringify": "^1.0.1"
           }
         },
         "ansi-regex": {
           "version": "2.1.1",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "aproba": {
           "version": "1.1.1",
@@ -5582,8 +5848,7 @@
         "balanced-match": {
           "version": "0.4.2",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "bcrypt-pbkdf": {
           "version": "1.0.1",
@@ -5598,7 +5863,6 @@
           "version": "0.0.9",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "inherits": "~2.0.0"
           }
@@ -5607,7 +5871,6 @@
           "version": "2.10.1",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "hoek": "2.x.x"
           }
@@ -5616,7 +5879,6 @@
           "version": "1.1.7",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "balanced-match": "^0.4.1",
             "concat-map": "0.0.1"
@@ -5625,8 +5887,7 @@
         "buffer-shims": {
           "version": "1.0.0",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "caseless": {
           "version": "0.12.0",
@@ -5643,14 +5904,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"
           }
@@ -5658,20 +5917,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",
@@ -5717,8 +5973,7 @@
         "delayed-stream": {
           "version": "1.0.0",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "delegates": {
           "version": "1.0.0",
@@ -5732,7 +5987,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "jsbn": "0.1.1"
+            "jsbn": "~0.1.0"
           }
         },
         "extend": {
@@ -5744,8 +5999,7 @@
         "extsprintf": {
           "version": "1.0.2",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "forever-agent": {
           "version": "0.6.1",
@@ -5767,14 +6021,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.2",
             "inherits": "~2.0.0",
@@ -5830,7 +6082,6 @@
           "version": "7.1.2",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "fs.realpath": "^1.0.0",
             "inflight": "^1.0.4",
@@ -5843,8 +6094,7 @@
         "graceful-fs": {
           "version": "4.1.11",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "har-schema": {
           "version": "1.0.5",
@@ -5858,8 +6108,8 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "ajv": "4.11.8",
-            "har-schema": "1.0.5"
+            "ajv": "^4.9.1",
+            "har-schema": "^1.0.5"
           }
         },
         "has-unicode": {
@@ -5883,8 +6133,7 @@
         "hoek": {
           "version": "2.16.3",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "http-signature": {
           "version": "1.1.1",
@@ -5901,7 +6150,6 @@
           "version": "1.0.6",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "once": "^1.3.0",
             "wrappy": "1"
@@ -5910,8 +6158,7 @@
         "inherits": {
           "version": "2.0.3",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "ini": {
           "version": "1.3.4",
@@ -5923,7 +6170,6 @@
           "version": "1.0.0",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
@@ -5937,8 +6183,7 @@
         "isarray": {
           "version": "1.0.0",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "isstream": {
           "version": "0.1.2",
@@ -5952,7 +6197,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "jsbn": "0.1.1"
+            "jsbn": "~0.1.0"
           }
         },
         "jsbn": {
@@ -5973,7 +6218,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "jsonify": "0.0.0"
+            "jsonify": "~0.0.0"
           }
         },
         "json-stringify-safe": {
@@ -6011,14 +6256,12 @@
         "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"
           }
@@ -6027,7 +6270,6 @@
           "version": "3.0.4",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
@@ -6035,14 +6277,12 @@
         "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"
           }
@@ -6095,8 +6335,7 @@
         "number-is-nan": {
           "version": "1.0.1",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "oauth-sign": {
           "version": "0.8.2",
@@ -6114,7 +6353,6 @@
           "version": "1.4.0",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "wrappy": "1"
           }
@@ -6144,8 +6382,7 @@
         "path-is-absolute": {
           "version": "1.0.1",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "performance-now": {
           "version": "0.2.0",
@@ -6156,8 +6393,7 @@
         "process-nextick-args": {
           "version": "1.0.7",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "punycode": {
           "version": "1.4.1",
@@ -6195,7 +6431,6 @@
           "version": "2.2.9",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "buffer-shims": "~1.0.0",
             "core-util-is": "~1.0.0",
@@ -6240,7 +6475,6 @@
           "version": "2.6.1",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "glob": "^7.0.5"
           }
@@ -6248,8 +6482,7 @@
         "safe-buffer": {
           "version": "5.0.1",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "semver": {
           "version": "5.3.0",
@@ -6307,7 +6540,6 @@
           "version": "1.0.2",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -6318,7 +6550,6 @@
           "version": "1.0.1",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "safe-buffer": "^5.0.1"
           }
@@ -6333,7 +6564,6 @@
           "version": "3.0.1",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "ansi-regex": "^2.0.0"
           }
@@ -6348,7 +6578,6 @@
           "version": "2.2.1",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "block-stream": "*",
             "fstream": "^1.0.2",
@@ -6404,8 +6633,7 @@
         "util-deprecate": {
           "version": "1.0.2",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "uuid": {
           "version": "3.0.1",
@@ -6434,15 +6662,14 @@
         "wrappy": {
           "version": "1.0.2",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         }
       }
     },
     "fstream": {
       "version": "1.0.12",
       "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
-      "integrity": "sha1-Touo7i1Ivk99DeUFRVVI6uWTIEU=",
+      "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
       "requires": {
         "graceful-fs": "^4.1.2",
         "inherits": "~2.0.0",
@@ -6500,7 +6727,7 @@
     "gaze": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
-      "integrity": "sha1-xEFzPhO5J6yMD/C0w7Az8ogSkko=",
+      "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
       "requires": {
         "globule": "^1.0.0"
       }
@@ -6570,7 +6797,6 @@
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
       "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
       "dev": true,
-      "optional": true,
       "requires": {
         "is-glob": "^2.0.0"
       }
@@ -6614,7 +6840,7 @@
     "globule": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
-      "integrity": "sha1-Xf+xsZHyLSB5epNptJ6rTpg5aW0=",
+      "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
       "requires": {
         "glob": "~7.1.1",
         "lodash": "~4.17.10",
@@ -7820,8 +8046,7 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
       "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "is-finite": {
       "version": "1.0.2",
@@ -7841,7 +8066,6 @@
       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
       "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
       "dev": true,
-      "optional": true,
       "requires": {
         "is-extglob": "^1.0.0"
       }
@@ -7906,8 +8130,7 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
       "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "is-promise": {
       "version": "2.1.0",
@@ -8070,7 +8293,7 @@
     "js-base64": {
       "version": "2.5.1",
       "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
-      "integrity": "sha1-Hvo57yxfeYC7F4St5KivLeMpESE="
+      "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw=="
     },
     "js-file-download": {
       "version": "0.4.4",
@@ -8459,8 +8682,7 @@
             "ansi-regex": {
               "version": "2.1.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "aproba": {
               "version": "1.2.0",
@@ -8481,14 +8703,12 @@
             "balanced-match": {
               "version": "1.0.0",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "brace-expansion": {
               "version": "1.1.11",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "balanced-match": "^1.0.0",
                 "concat-map": "0.0.1"
@@ -8503,20 +8723,17 @@
             "code-point-at": {
               "version": "1.1.0",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "concat-map": {
               "version": "0.0.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "console-control-strings": {
               "version": "1.1.0",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "core-util-is": {
               "version": "1.0.2",
@@ -8617,7 +8834,7 @@
               "dev": true,
               "optional": true,
               "requires": {
-                "minimatch": "3.0.4"
+                "minimatch": "^3.0.4"
               }
             },
             "inflight": {
@@ -8633,8 +8850,7 @@
             "inherits": {
               "version": "2.0.3",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "ini": {
               "version": "1.3.5",
@@ -8646,9 +8862,8 @@
               "version": "1.0.0",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
-                "number-is-nan": "1.0.1"
+                "number-is-nan": "^1.0.0"
               }
             },
             "isarray": {
@@ -8661,7 +8876,6 @@
               "version": "3.0.4",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "brace-expansion": "^1.1.7"
               }
@@ -8669,14 +8883,12 @@
             "minimist": {
               "version": "0.0.8",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "minipass": {
               "version": "2.2.4",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "safe-buffer": "^5.1.1",
                 "yallist": "^3.0.0"
@@ -8695,7 +8907,6 @@
               "version": "0.5.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "minimist": "0.0.8"
               }
@@ -8757,8 +8968,8 @@
               "dev": true,
               "optional": true,
               "requires": {
-                "ignore-walk": "3.0.1",
-                "npm-bundled": "1.0.3"
+                "ignore-walk": "^3.0.1",
+                "npm-bundled": "^1.0.1"
               }
             },
             "npmlog": {
@@ -8776,8 +8987,7 @@
             "number-is-nan": {
               "version": "1.0.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "object-assign": {
               "version": "4.1.1",
@@ -8789,7 +8999,6 @@
               "version": "1.4.0",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "wrappy": "1"
               }
@@ -8875,8 +9084,7 @@
             "safe-buffer": {
               "version": "5.1.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "safer-buffer": {
               "version": "2.1.2",
@@ -8912,7 +9120,6 @@
               "version": "1.0.2",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "code-point-at": "^1.0.0",
                 "is-fullwidth-code-point": "^1.0.0",
@@ -8932,7 +9139,6 @@
               "version": "3.0.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "ansi-regex": "^2.0.0"
               }
@@ -8976,14 +9182,12 @@
             "wrappy": {
               "version": "1.0.2",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "yallist": {
               "version": "3.0.2",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             }
           }
         },
@@ -9554,8 +9758,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
       "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "loose-envify": {
       "version": "1.3.1",
@@ -10100,7 +10303,7 @@
     "node-fetch": {
       "version": "1.7.3",
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
-      "integrity": "sha1-mA9vcthSEaU0fGsrwYxbhMPrR+8=",
+      "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
       "requires": {
         "encoding": "^0.1.11",
         "is-stream": "^1.0.1"
@@ -10115,7 +10318,7 @@
     "node-gyp": {
       "version": "3.8.0",
       "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
-      "integrity": "sha1-VAMEJhwzDoDQ1e3OJTpoyzlkIYw=",
+      "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
       "requires": {
         "fstream": "^1.0.0",
         "glob": "^7.0.3",
@@ -10134,7 +10337,7 @@
         "ajv": {
           "version": "6.10.0",
           "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
-          "integrity": "sha1-kNDVRDnaWHzX6EO/twRfUL0ivfE=",
+          "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
           "requires": {
             "fast-deep-equal": "^2.0.1",
             "fast-json-stable-stringify": "^2.0.0",
@@ -10145,12 +10348,12 @@
         "aws4": {
           "version": "1.8.0",
           "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-          "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8="
+          "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
         },
         "extend": {
           "version": "3.0.2",
           "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-          "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo="
+          "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
         },
         "fast-deep-equal": {
           "version": "2.0.1",
@@ -10160,7 +10363,7 @@
         "har-validator": {
           "version": "5.1.3",
           "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-          "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=",
+          "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
           "requires": {
             "ajv": "^6.5.5",
             "har-schema": "^2.0.0"
@@ -10169,17 +10372,17 @@
         "json-schema-traverse": {
           "version": "0.4.1",
           "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-          "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA="
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
         },
         "mime-db": {
           "version": "1.40.0",
           "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
-          "integrity": "sha1-plBX6ZjbCQ9zKmj2wnbTh9QSbDI="
+          "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
         },
         "mime-types": {
           "version": "2.1.24",
           "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
-          "integrity": "sha1-tvjQs+lR77d97eyhlM/20W9nb4E=",
+          "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
           "requires": {
             "mime-db": "1.40.0"
           }
@@ -10187,17 +10390,17 @@
         "oauth-sign": {
           "version": "0.9.0",
           "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-          "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU="
+          "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
         },
         "psl": {
           "version": "1.1.32",
           "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz",
-          "integrity": "sha1-PxMnF88vnBaXJLK2yvNzz2lBmNs="
+          "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g=="
         },
         "request": {
           "version": "2.88.0",
           "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-          "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=",
+          "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
           "requires": {
             "aws-sign2": "~0.7.0",
             "aws4": "^1.8.0",
@@ -10224,7 +10427,7 @@
         "safe-buffer": {
           "version": "5.1.2",
           "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0="
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
         },
         "semver": {
           "version": "5.3.0",
@@ -10234,7 +10437,7 @@
         "tough-cookie": {
           "version": "2.4.3",
           "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-          "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=",
+          "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
           "requires": {
             "psl": "^1.1.24",
             "punycode": "^1.4.1"
@@ -10243,7 +10446,7 @@
         "uuid": {
           "version": "3.3.2",
           "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
-          "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE="
+          "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
         }
       }
     },
@@ -10325,7 +10528,7 @@
     "node-sass": {
       "version": "4.12.0",
       "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
-      "integrity": "sha1-CRT1MZMjgBFKMMxfpPpjIzol8Bc=",
+      "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
       "requires": {
         "async-foreach": "^0.1.3",
         "chalk": "^1.1.1",
@@ -10349,7 +10552,7 @@
         "ajv": {
           "version": "6.10.0",
           "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
-          "integrity": "sha1-kNDVRDnaWHzX6EO/twRfUL0ivfE=",
+          "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
           "requires": {
             "fast-deep-equal": "^2.0.1",
             "fast-json-stable-stringify": "^2.0.0",
@@ -10360,7 +10563,7 @@
         "aws4": {
           "version": "1.8.0",
           "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-          "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8="
+          "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
         },
         "cross-spawn": {
           "version": "3.0.1",
@@ -10374,7 +10577,7 @@
         "extend": {
           "version": "3.0.2",
           "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-          "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo="
+          "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
         },
         "fast-deep-equal": {
           "version": "2.0.1",
@@ -10384,7 +10587,7 @@
         "har-validator": {
           "version": "5.1.3",
           "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-          "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=",
+          "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
           "requires": {
             "ajv": "^6.5.5",
             "har-schema": "^2.0.0"
@@ -10393,22 +10596,22 @@
         "json-schema-traverse": {
           "version": "0.4.1",
           "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-          "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA="
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
         },
         "lodash": {
           "version": "4.17.11",
           "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
-          "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40="
+          "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
         },
         "mime-db": {
           "version": "1.40.0",
           "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
-          "integrity": "sha1-plBX6ZjbCQ9zKmj2wnbTh9QSbDI="
+          "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
         },
         "mime-types": {
           "version": "2.1.24",
           "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
-          "integrity": "sha1-tvjQs+lR77d97eyhlM/20W9nb4E=",
+          "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
           "requires": {
             "mime-db": "1.40.0"
           }
@@ -10421,17 +10624,17 @@
         "oauth-sign": {
           "version": "0.9.0",
           "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-          "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU="
+          "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
         },
         "psl": {
           "version": "1.1.32",
           "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz",
-          "integrity": "sha1-PxMnF88vnBaXJLK2yvNzz2lBmNs="
+          "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g=="
         },
         "request": {
           "version": "2.88.0",
           "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-          "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=",
+          "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
           "requires": {
             "aws-sign2": "~0.7.0",
             "aws4": "^1.8.0",
@@ -10458,12 +10661,12 @@
         "safe-buffer": {
           "version": "5.1.2",
           "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0="
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
         },
         "tough-cookie": {
           "version": "2.4.3",
           "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-          "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=",
+          "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
           "requires": {
             "psl": "^1.1.24",
             "punycode": "^1.4.1"
@@ -10472,7 +10675,7 @@
         "uuid": {
           "version": "3.3.2",
           "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
-          "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE="
+          "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
         }
       }
     },
@@ -13227,7 +13430,7 @@
     "npmlog": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-      "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
       "requires": {
         "are-we-there-yet": "~1.1.2",
         "console-control-strings": "~1.1.0",
@@ -13497,7 +13700,7 @@
     "osenv": {
       "version": "0.1.5",
       "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
-      "integrity": "sha1-hc36+uso6Gd/QW4odZK18/SepBA=",
+      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
       "requires": {
         "os-homedir": "^1.0.0",
         "os-tmpdir": "^1.0.0"
@@ -13730,7 +13933,7 @@
     "path-parse": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw="
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
     },
     "path-to-regexp": {
       "version": "0.1.7",
@@ -14005,7 +14208,7 @@
     "promise": {
       "version": "7.3.1",
       "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
-      "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
+      "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
       "requires": {
         "asap": "~2.0.3"
       }
@@ -14360,6 +14563,15 @@
         "scheduler": "^0.11.2"
       }
     },
+    "react-event-timeline": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/react-event-timeline/-/react-event-timeline-1.6.3.tgz",
+      "integrity": "sha512-hMGhC9/Xx3sPF/TSlMCA13hZm/2c5CvBxbkDM7bQ4yq6VJ6AmhjqKPnU6/3nVmWUGpK3YqhHb95OiqulxVD3Eg==",
+      "dev": true,
+      "requires": {
+        "prop-types": "^15.6.0"
+      }
+    },
     "react-fa": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/react-fa/-/react-fa-5.0.0.tgz",
@@ -14600,7 +14812,7 @@
     "react-spinners": {
       "version": "0.5.4",
       "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.5.4.tgz",
-      "integrity": "sha1-WBZvi/hMvwbdesytS5SleXz+qbk=",
+      "integrity": "sha512-jo7BE8prvnZNL7xNrQL16geVXH6LmaWrhvvXnmUwz2MhFO14bhlAdCLuKCwqmUJ37kGphH0C2CJRThwkjfpVzw==",
       "requires": {
         "@emotion/core": "^10.0.4",
         "prop-types": "^15.5.10",
@@ -14648,7 +14860,7 @@
           "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
           "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
           "requires": {
-            "js-tokens": "3.0.2"
+            "js-tokens": "^3.0.0 || ^4.0.0"
           }
         }
       }
@@ -14751,7 +14963,7 @@
     "recompose": {
       "version": "0.30.0",
       "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz",
-      "integrity": "sha1-gnc2QbOSfox9JKDYfWWu66GKq9A=",
+      "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==",
       "requires": {
         "@babel/runtime": "^7.0.0",
         "change-emitter": "^0.1.2",
@@ -15047,7 +15259,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",
@@ -15123,6 +15335,11 @@
         "aproba": "^1.1.1"
       }
     },
+    "rw": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+      "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
+    },
     "rxjs": {
       "version": "6.3.3",
       "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
@@ -15242,7 +15459,7 @@
     "sass-loader": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz",
-      "integrity": "sha1-Fv1ROMuLQkv4p1lSihly1yqtBp0=",
+      "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==",
       "requires": {
         "clone-deep": "^2.0.1",
         "loader-utils": "^1.0.1",
@@ -15255,12 +15472,12 @@
         "big.js": {
           "version": "5.2.2",
           "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
-          "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg="
+          "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
         },
         "json5": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-          "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=",
+          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
           "requires": {
             "minimist": "^1.2.0"
           }
@@ -15268,7 +15485,7 @@
         "loader-utils": {
           "version": "1.2.3",
           "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
-          "integrity": "sha1-H/XcaRHJ8KBiUxpMBLYJQGEIwsc=",
+          "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
           "requires": {
             "big.js": "^5.2.2",
             "emojis-list": "^2.0.0",
@@ -15283,7 +15500,7 @@
         "semver": {
           "version": "5.7.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
-          "integrity": "sha1-eQp89v6lRZuslhELKbYEEtyP+Ws="
+          "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA=="
         }
       }
     },
@@ -15525,7 +15742,7 @@
     "shallow-clone": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz",
-      "integrity": "sha1-RIDNBuiC72iyrYij6lSDLixItXE=",
+      "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==",
       "requires": {
         "is-extendable": "^0.1.1",
         "kind-of": "^5.0.0",
@@ -15535,7 +15752,7 @@
         "kind-of": {
           "version": "5.1.0",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0="
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
         }
       }
     },
@@ -16027,7 +16244,7 @@
     "stdout-stream": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
-      "integrity": "sha1-WsF0zdXNcmEEqgwLK9g4FdjVNd4=",
+      "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
       "requires": {
         "readable-stream": "^2.0.1"
       },
@@ -16040,12 +16257,12 @@
         "process-nextick-args": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o="
+          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
         },
         "readable-stream": {
           "version": "2.3.6",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
+          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "requires": {
             "core-util-is": "~1.0.0",
             "inherits": "~2.0.3",
@@ -16059,7 +16276,7 @@
         "string_decoder": {
           "version": "1.1.1",
           "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "requires": {
             "safe-buffer": "~5.1.0"
           }
@@ -16397,7 +16614,7 @@
     "tar": {
       "version": "2.2.2",
       "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
-      "integrity": "sha1-DKiEhWLHKZuLRG/2pNYM27I+3EA=",
+      "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
       "requires": {
         "block-stream": "*",
         "fstream": "^1.0.12",
@@ -16588,7 +16805,7 @@
     "true-case-path": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
-      "integrity": "sha1-+BO1qMhrQNpZYGcisUTjIleZ9H0=",
+      "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
       "requires": {
         "glob": "^7.1.2"
       }
@@ -17069,7 +17286,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",
@@ -19045,8 +19262,7 @@
             "ansi-regex": {
               "version": "2.1.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "aproba": {
               "version": "1.2.0",
@@ -19089,8 +19305,7 @@
             "code-point-at": {
               "version": "1.1.0",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "concat-map": {
               "version": "0.0.1",
@@ -19101,8 +19316,7 @@
             "console-control-strings": {
               "version": "1.1.0",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "core-util-is": {
               "version": "1.0.2",
@@ -19219,8 +19433,7 @@
             "inherits": {
               "version": "2.0.3",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "ini": {
               "version": "1.3.5",
@@ -19232,7 +19445,6 @@
               "version": "1.0.0",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "number-is-nan": "^1.0.0"
               }
@@ -19249,20 +19461,18 @@
               "dev": true,
               "optional": true,
               "requires": {
-                "brace-expansion": "1.1.11"
+                "brace-expansion": "^1.1.7"
               }
             },
             "minimist": {
               "version": "0.0.8",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "minipass": {
               "version": "2.2.4",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "safe-buffer": "^5.1.1",
                 "yallist": "^3.0.0"
@@ -19281,7 +19491,6 @@
               "version": "0.5.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "minimist": "0.0.8"
               }
@@ -19362,8 +19571,7 @@
             "number-is-nan": {
               "version": "1.0.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "object-assign": {
               "version": "4.1.1",
@@ -19375,7 +19583,6 @@
               "version": "1.4.0",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "wrappy": "1"
               }
@@ -19461,8 +19668,7 @@
             "safe-buffer": {
               "version": "5.1.1",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "safer-buffer": {
               "version": "2.1.2",
@@ -19498,7 +19704,6 @@
               "version": "1.0.2",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "code-point-at": "^1.0.0",
                 "is-fullwidth-code-point": "^1.0.0",
@@ -19518,7 +19723,6 @@
               "version": "3.0.1",
               "bundled": true,
               "dev": true,
-              "optional": true,
               "requires": {
                 "ansi-regex": "^2.0.0"
               }
@@ -19562,14 +19766,12 @@
             "wrappy": {
               "version": "1.0.2",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             },
             "yallist": {
               "version": "3.0.2",
               "bundled": true,
-              "dev": true,
-              "optional": true
+              "dev": true
             }
           }
         },
@@ -19979,7 +20181,7 @@
     "whatwg-fetch": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
-      "integrity": "sha1-/IBORYzEYACbGiuWa8iBfSV4rvs="
+      "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
     },
     "which": {
       "version": "1.3.0",
@@ -19998,7 +20200,7 @@
     "wide-align": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
-      "integrity": "sha1-rgdOa9wMFKQx6ATmJFScYzsABFc=",
+      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
       "requires": {
         "string-width": "^1.0.2 || 2"
       }
diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json
index a76541e36..4da085836 100644
--- a/monkey/monkey_island/cc/ui/package.json
+++ b/monkey/monkey_island/cc/ui/package.json
@@ -55,6 +55,7 @@
     "null-loader": "^0.1.1",
     "phantomjs-prebuilt": "^2.1.16",
     "react-addons-test-utils": "^15.6.2",
+    "react-event-timeline": "^1.6.3",
     "react-hot-loader": "^4.3.11",
     "rimraf": "^2.6.2",
     "style-loader": "^0.22.1",
@@ -64,12 +65,15 @@
     "webpack-dev-server": "^3.1.9"
   },
   "dependencies": {
+    "@emotion/core": "^10.0.10",
     "@kunukn/react-collapse": "^1.0.5",
-    "classnames": "^2.2.6",
     "bootstrap": "3.4.1",
+    "classnames": "^2.2.6",
     "core-js": "^2.5.7",
+    "d3": "^5.11.0",
     "downloadjs": "^1.4.7",
     "fetch": "^1.1.0",
+    "file-saver": "^2.0.2",
     "filepond": "^4.2.0",
     "js-file-download": "^0.4.4",
     "json-loader": "^0.5.7",
@@ -84,6 +88,7 @@
     "react-bootstrap": "^0.32.4",
     "react-copy-to-clipboard": "^5.0.1",
     "react-data-components": "^1.2.0",
+    "react-desktop-notification": "^1.0.9",
     "react-dimensions": "^1.3.0",
     "react-dom": "^16.5.2",
     "react-fa": "^5.0.0",
@@ -93,14 +98,13 @@
     "react-jsonschema-form": "^1.0.5",
     "react-redux": "^5.1.1",
     "react-router-dom": "^4.3.1",
+    "react-spinners": "^0.5.4",
     "react-table": "^6.8.6",
     "react-toggle": "^4.0.1",
     "react-tooltip-lite": "^1.9.1",
     "redux": "^4.0.0",
     "sass-loader": "^7.1.0",
     "sha3": "^2.0.0",
-    "react-spinners": "^0.5.4",
-    "@emotion/core": "^10.0.10",
-    "react-desktop-notification": "^1.0.9"
+    "pluralize": "^7.0.0"
   }
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js
index 619d5e922..09038292e 100644
--- a/monkey/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey/monkey_island/cc/ui/src/components/Main.js
@@ -7,10 +7,10 @@ import RunServerPage from 'components/pages/RunServerPage';
 import ConfigurePage from 'components/pages/ConfigurePage';
 import RunMonkeyPage from 'components/pages/RunMonkeyPage';
 import MapPage from 'components/pages/MapPage';
-import PassTheHashMapPage from 'components/pages/PassTheHashMapPage';
 import TelemetryPage from 'components/pages/TelemetryPage';
 import StartOverPage from 'components/pages/StartOverPage';
 import ReportPage from 'components/pages/ReportPage';
+import ZeroTrustReportPage from 'components/pages/ZeroTrustReportPage';
 import LicensePage from 'components/pages/LicensePage';
 import AuthComponent from 'components/AuthComponent';
 import LoginPageComponent from 'components/pages/LoginPage';
@@ -29,6 +29,8 @@ let infectionMonkeyImage = require('../images/infection-monkey.svg');
 let guardicoreLogoImage = require('../images/guardicore-logo.png');
 let notificationIcon = require('../images/notification-logo-512x512.png');
 
+const reportZeroTrustRoute = '/report/zero_trust';
+
 class AppComponent extends AuthComponent {
   updateStatus = () => {
     this.auth.loggedIn()
@@ -148,7 +150,7 @@ class AppComponent extends AuthComponent {
                   </NavLink>
                 </li>
                 <li>
-                  <NavLink to="/report">
+                  <NavLink to="/report/security">
                     <span className="number">4.</span>
                     Security Report
                     {this.state.completedSteps.report_done ?
@@ -156,6 +158,15 @@ class AppComponent extends AuthComponent {
                       : ''}
                   </NavLink>
                 </li>
+                <li>
+                  <NavLink to="/report/zero_trust">
+                    <span className="number">5.</span>
+                    Zero Trust Report
+                    {this.state.completedSteps.report_done ?
+                      <Icon name="check" className="pull-right checkmark text-success"/>
+                      : ''}
+                  </NavLink>
+                </li>
                 <li>
                   <NavLink to="/start-over">
                     <span className="number"><i className="fa fa-undo" style={{'marginLeft': '-1px'}}/></span>
@@ -190,7 +201,8 @@ class AppComponent extends AuthComponent {
               {this.renderRoute('/infection/map', <MapPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/infection/telemetry', <TelemetryPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/start-over', <StartOverPage onStatusChange={this.updateStatus}/>)}
-              {this.renderRoute('/report', <ReportPage onStatusChange={this.updateStatus}/>)}
+              {this.renderRoute('/report/security', <ReportPage onStatusChange={this.updateStatus}/>)}
+              {this.renderRoute(reportZeroTrustRoute, <ZeroTrustReportPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/license', <LicensePage onStatusChange={this.updateStatus}/>)}
             </Col>
           </Row>
@@ -200,10 +212,11 @@ class AppComponent extends AuthComponent {
   }
 
   showInfectionDoneNotification() {
-    if (this.state.completedSteps.infection_done) {
-      let hostname = window.location.hostname;
-      let url = `https://${hostname}:5000/report`;
-      console.log("Trying to show notification. URL: " + url + " | icon: " + notificationIcon);
+    if (this.shouldShowNotification()) {
+      const hostname = window.location.hostname;
+      const port = window.location.port;
+      const protocol = window.location.protocol;
+      const url = `${protocol}//${hostname}:${port}${reportZeroTrustRoute}`;
 
       Notifier.start(
         "Monkey Island",
@@ -212,6 +225,11 @@ class AppComponent extends AuthComponent {
         notificationIcon);
     }
   }
+
+  shouldShowNotification() {
+    // No need to show the notification to redirect to the report if we're already in the report page
+    return (this.state.completedSteps.infection_done && !window.location.pathname.startsWith("/report"));
+  }
 }
 
 AppComponent.defaultProps = {};
diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js
index 07fd4a400..24d742c14 100644
--- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js
+++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js
@@ -1,7 +1,7 @@
 import React from 'react';
 import '../../../styles/Collapse.scss'
-import '../../report-components/StolenPasswords'
-import StolenPasswordsComponent from "../../report-components/StolenPasswords";
+import '../../report-components/security/StolenPasswords'
+import StolenPasswordsComponent from "../../report-components/security/StolenPasswords";
 import {ScanStatus} from "./Helpers"
 
 
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 207ae8699..68ba84aa6 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -1,20 +1,27 @@
-import React from 'react';
+import React, {Fragment} from 'react';
 import {Button, Col} from 'react-bootstrap';
-import BreachedServers from 'components/report-components/BreachedServers';
-import ScannedServers from 'components/report-components/ScannedServers';
-import PostBreach from 'components/report-components/PostBreach';
+import BreachedServers from 'components/report-components/security/BreachedServers';
+import ScannedServers from 'components/report-components/security/ScannedServers';
+import PostBreach from 'components/report-components/security/PostBreach';
 import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
 import {edgeGroupToColor, options} from 'components/map/MapOptions';
-import StolenPasswords from 'components/report-components/StolenPasswords';
-import CollapsibleWellComponent from 'components/report-components/CollapsibleWell';
+import StolenPasswords from 'components/report-components/security/StolenPasswords';
+import CollapsibleWellComponent from 'components/report-components/security/CollapsibleWell';
 import {Line} from 'rc-progress';
 import AuthComponent from '../AuthComponent';
 import PassTheHashMapPageComponent from "./PassTheHashMapPage";
-import StrongUsers from "components/report-components/StrongUsers";
-import AttackReport from "components/report-components/AttackReport";
+import StrongUsers from "components/report-components/security/StrongUsers";
+import AttackReport from "components/report-components/security/AttackReport";
+import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader";
+import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning";
+import ReportLoader from "../report-components/common/ReportLoader";
+import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning";
+import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlance";
+import PrintReportButton from "../report-components/common/PrintReportButton";
+import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus";
 
 let guardicoreLogoImage = require('../../images/guardicore-logo.png');
-let monkeyLogoImage = require('../../images/monkey-icon.svg');
+
 
 class ReportPageComponent extends AuthComponent {
 
@@ -66,18 +73,11 @@ class ReportPageComponent extends AuthComponent {
 
   render() {
     let content;
-    if (Object.keys(this.state.report).length === 0) {
-      if (this.state.runStarted) {
-        content = (<h1>Generating Report...</h1>);
-      } else {
-        content =
-          <p className="alert alert-warning">
-            <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
-            You have to run a monkey before generating a report!
-          </p>;
-      }
-    } else {
+
+    if (this.state.runStarted) {
       content = this.generateReportContent();
+    } else {
+      content = <MustRunMonkeyWarning/>;
     }
 
     return (
@@ -90,15 +90,15 @@ class ReportPageComponent extends AuthComponent {
     );
   }
 
+  stillLoadingDataFromServer() {
+    return Object.keys(this.state.report).length === 0;
+  }
+
   updateMonkeysRunning = () => {
     return this.authFetch('/api')
       .then(res => res.json())
       .then(res => {
-        // This check is used to prevent unnecessary re-rendering
-        this.setState({
-          allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']),
-          runStarted: res['completed_steps']['run_monkey']
-        });
+        this.setState(extractExecutionStatusFromServerResponse(res));
         return res;
       });
   };
@@ -117,7 +117,7 @@ class ReportPageComponent extends AuthComponent {
 
   getReportFromServer(res) {
     if (res['completed_steps']['run_monkey']) {
-      this.authFetch('/api/report')
+      this.authFetch('/api/report/security')
         .then(res => res.json())
         .then(res => {
           this.setState({
@@ -128,49 +128,36 @@ class ReportPageComponent extends AuthComponent {
   }
 
   generateReportContent() {
+    let content;
+
+    if (this.stillLoadingDataFromServer()) {
+      content = <ReportLoader loading={true}/>;
+    } else {
+      content =
+        <div>
+            {this.generateReportOverviewSection()}
+            {this.generateReportFindingsSection()}
+            {this.generateReportRecommendationsSection()}
+            {this.generateReportGlanceSection()}
+            {this.generateAttackSection()}
+            {this.generateReportFooter()}
+        </div>;
+    }
+
     return (
-      <div>
-        <div className="text-center no-print" style={{marginBottom: '20px'}}>
-          <Button bsSize="large" onClick={() => {
-            print();
-          }}><i className="glyphicon glyphicon-print"/> Print Report</Button>
+      <Fragment>
+        <div style={{marginBottom: '20px'}}>
+          <PrintReportButton onClick={() => {print();}} />
         </div>
         <div className="report-page">
-          {this.generateReportHeader()}
+          <ReportHeader report_type={ReportTypes.security}/>
           <hr/>
-          {this.generateReportOverviewSection()}
-          {this.generateReportFindingsSection()}
-          {this.generateReportRecommendationsSection()}
-          {this.generateReportGlanceSection()}
-          {this.generateAttackSection()}
-          {this.generateReportFooter()}
+          {content}
         </div>
-        <div className="text-center no-print" style={{marginTop: '20px'}}>
-          <Button bsSize="large" onClick={() => {
-            print();
-          }}><i className="glyphicon glyphicon-print"/> Print Report</Button>
+        <div style={{marginTop: '20px'}}>
+          <PrintReportButton onClick={() => {print();}} />
         </div>
-      </div>
-    );
-  }
-
-  generateReportHeader() {
-    return (
-      <div id="header" className="row justify-content-between">
-        <Col xs={8}>
-          <div>
-            <h1 style={{marginTop: '0px', marginBottom: '5px', color: '#666666', fontFamily: 'Alegreya'}}>Security Report</h1>
-            <h1 style={{marginTop: '0px', marginBottom: '0px', color: '#ffcc00', fontFamily: 'Alegreya'}}>Infection <b>Monkey</b></h1>
-          </div>
-        </Col>
-        <Col xs={4}>
-          <img src={monkeyLogoImage}
-               style={{
-                 float: 'right',
-                 width: '80px'
-               }}/>
-        </Col>
-      </div>
+      </Fragment>
     );
   }
 
@@ -180,27 +167,8 @@ class ReportPageComponent extends AuthComponent {
         <h2>
           Overview
         </h2>
-        {
-          this.state.report.glance.exploited.length > 0 ?
-            (<p className="alert alert-danger">
-              <i className="glyphicon glyphicon-exclamation-sign" style={{'marginRight': '5px'}}/>
-              Critical security issues were detected!
-            </p>) :
-            (<p className="alert alert-success">
-              <i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/>
-              No critical security issues were detected.
-            </p>)
-        }
-        {
-          this.state.allMonkeysAreDead ?
-            ''
-            :
-            (<p className="alert alert-warning">
-              <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
-              Some monkeys are still running. To get the best report it's best to wait for all of them to finish
-              running.
-            </p>)
-        }
+        <SecurityIssuesGlance issuesFound={this.state.report.glance.exploited.length > 0}/>
+        <MonkeysStillAliveWarning allMonkeysAreDead={this.state.allMonkeysAreDead}/>
         {
           this.state.report.glance.exploited.length > 0 ?
             ''
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js
new file mode 100755
index 000000000..a0b92d9bd
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js
@@ -0,0 +1,134 @@
+import React, {Fragment} from 'react';
+import {Col} from 'react-bootstrap';
+import AuthComponent from '../AuthComponent';
+import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader";
+import ReportLoader from "../report-components/common/ReportLoader";
+import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning";
+import PrintReportButton from "../report-components/common/PrintReportButton";
+import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus";
+import SummarySection from "../report-components/zerotrust/SummarySection";
+import FindingsSection from "../report-components/zerotrust/FindingsSection";
+import PrinciplesSection from "../report-components/zerotrust/PrinciplesSection";
+
+class ZeroTrustReportPageComponent extends AuthComponent {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      allMonkeysAreDead: false,
+      runStarted: true
+    };
+  }
+
+  componentDidMount() {
+    this.updatePageState();
+    const refreshInterval = setInterval(this.updatePageState, 8000);
+    this.setState(
+      {refreshDataIntervalHandler: refreshInterval}
+    )
+  }
+
+  componentWillUnmount() {
+    clearInterval(this.state.refreshDataIntervalHandler);
+  }
+
+  updateMonkeysRunning = () => {
+    return this.authFetch('/api')
+      .then(res => res.json())
+      .then(res => {
+        this.setState(extractExecutionStatusFromServerResponse(res));
+        return res;
+      });
+  };
+
+  updatePageState = () => {
+    this.updateMonkeysRunning().then(res => this.getZeroTrustReportFromServer(res));
+  };
+
+  render() {
+    let content;
+    if (this.state.runStarted) {
+      content = this.generateReportContent();
+    } else {
+      content = <MustRunMonkeyWarning/>;
+    }
+
+    return (
+      <Col xs={12} lg={10}>
+        <h1 className="page-title no-print">5. Zero Trust Report</h1>
+        <div style={{'fontSize': '1.2em'}}>
+          {content}
+        </div>
+      </Col>
+    );
+  }
+
+  generateReportContent() {
+    let content;
+
+    if (this.stillLoadingDataFromServer()) {
+      content = <ReportLoader loading={true}/>;
+    } else {
+      content = <div id="MainContentSection">
+        <SummarySection allMonkeysAreDead={this.state.allMonkeysAreDead} pillars={this.state.pillars}/>
+        <PrinciplesSection principles={this.state.principles}
+                           pillarsToStatuses={this.state.pillars.pillarsToStatuses}/>
+        <FindingsSection pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/>
+      </div>;
+    }
+
+    return (
+      <Fragment>
+        <div style={{marginBottom: '20px'}}>
+          <PrintReportButton onClick={() => {
+            print();
+          }}/>
+        </div>
+        <div className="report-page">
+          <ReportHeader report_type={ReportTypes.zeroTrust}/>
+          <hr/>
+          {content}
+        </div>
+        <div style={{marginTop: '20px'}}>
+          <PrintReportButton onClick={() => {
+            print();
+          }}/>
+        </div>
+      </Fragment>
+    )
+  }
+
+  stillLoadingDataFromServer() {
+    return typeof this.state.findings === "undefined"
+      || typeof this.state.pillars === "undefined"
+      || typeof this.state.principles === "undefined";
+  }
+
+  getZeroTrustReportFromServer() {
+    let res;
+    this.authFetch('/api/report/zero_trust/findings')
+      .then(res => res.json())
+      .then(res => {
+        this.setState({
+          findings: res
+        });
+      });
+    this.authFetch('/api/report/zero_trust/principles')
+      .then(res => res.json())
+      .then(res => {
+        this.setState({
+          principles: res
+        });
+      });
+    this.authFetch('/api/report/zero_trust/pillars')
+      .then(res => res.json())
+      .then(res => {
+        this.setState({
+          pillars: res
+        });
+      });
+  }
+}
+
+export default ZeroTrustReportPageComponent;
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ExecutionStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ExecutionStatus.js
new file mode 100644
index 000000000..840e570d7
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ExecutionStatus.js
@@ -0,0 +1,6 @@
+export function extractExecutionStatusFromServerResponse(res) {
+  return {
+    allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']),
+    runStarted: res['completed_steps']['run_monkey']
+  };
+}
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js
new file mode 100644
index 000000000..7b72570fa
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js
@@ -0,0 +1,21 @@
+import React, {Component} from "react";
+import * as PropTypes from "prop-types";
+
+export default class MonkeysStillAliveWarning extends Component {
+  render() {
+    return <div>
+      {
+        this.props.allMonkeysAreDead ?
+          ''
+          :
+          (<p className="alert alert-warning">
+            <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
+            Some monkeys are still running. To get the best report it's best to wait for all of them to finish
+            running.
+          </p>)
+      }
+    </div>
+  }
+}
+
+MonkeysStillAliveWarning.propTypes = {allMonkeysAreDead: PropTypes.bool};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js
new file mode 100644
index 000000000..f1d23e302
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js
@@ -0,0 +1,11 @@
+import React, {Component} from "react";
+import {NavLink} from "react-router-dom";
+
+export default class MustRunMonkeyWarning extends Component {
+  render() {
+    return <p className="alert alert-warning">
+      <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
+      <b>You have to <NavLink to="/run-monkey">run a monkey</NavLink> before generating a report!</b>
+    </p>
+  }
+}
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js
new file mode 100644
index 000000000..5bc6183fd
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js
@@ -0,0 +1,36 @@
+import React, {Component} from "react";
+import ReactTable from "react-table";
+import * as PropTypes from "prop-types";
+
+class PaginatedTable extends Component {
+  render() {
+    if (this.props.data.length > 0) {
+      let defaultPageSize = this.props.data.length > this.props.pageSize ? this.props.pageSize : this.props.data.length;
+      let showPagination = this.props.data.length > this.props.pageSize;
+
+      return (
+        <div>
+          <ReactTable
+            columns={this.props.columns}
+            data={this.props.data}
+            showPagination={showPagination}
+            defaultPageSize={defaultPageSize}
+          />
+        </div>
+      );
+    }
+    else {
+      return (
+        <div/>
+      );
+    }
+  }
+}
+
+export default PaginatedTable;
+
+PaginatedTable.propTypes = {
+  data: PropTypes.array,
+  columns: PropTypes.array,
+  pageSize: PropTypes.number,
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js
new file mode 100644
index 000000000..1a692bd68
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js
@@ -0,0 +1,14 @@
+import React, {Component} from "react";
+import {Button} from "react-bootstrap";
+import * as PropTypes from "prop-types";
+
+export default class PrintReportButton extends Component {
+  render() {
+    return <div className="text-center no-print">
+      <Button bsSize="large" onClick={this.props.onClick}><i className="glyphicon glyphicon-print"/> Print
+        Report</Button>
+    </div>
+  }
+}
+
+PrintReportButton.propTypes = {onClick: PropTypes.func};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js
new file mode 100644
index 000000000..44d470f7e
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js
@@ -0,0 +1,45 @@
+import React, {Component} from "react";
+import {Col} from "react-bootstrap";
+import * as PropTypes from "prop-types";
+
+let monkeyLogoImage = require('../../../images/monkey-icon.svg');
+
+export const ReportTypes = {
+  zeroTrust: "Zero Trust",
+  security: "Security",
+  null: ""
+};
+
+export class ReportHeader extends Component {
+  report_type;
+
+  render() {
+    return <div id="header" className="row justify-content-between">
+      <Col xs={8}>
+        <div>
+          <h1 style={{marginTop: '0px', marginBottom: '5px', color: '#666666', fontFamily: 'Alegreya'}}>
+            {this.props.report_type} Report</h1>
+          <h1 style={{
+            marginTop: '0px',
+            marginBottom: '0px',
+            color: '#ffcc00',
+            fontFamily: 'Alegreya'
+          }}>Infection <b>Monkey</b></h1>
+        </div>
+      </Col>
+      <Col xs={4}>
+        <img src={monkeyLogoImage}
+             style={{
+               float: 'right',
+               width: '80px'
+             }}/>
+      </Col>
+    </div>
+  }
+}
+
+export default ReportHeader;
+
+ReportHeader.propTypes = {
+  report_type: PropTypes.string,
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js
new file mode 100644
index 000000000..e389f7532
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js
@@ -0,0 +1,28 @@
+import {css} from "@emotion/core";
+import React, {Component} from "react";
+import {GridLoader} from "react-spinners";
+import * as PropTypes from "prop-types";
+
+const loading_css_override = css`
+    display: block;
+    margin-right: auto;
+    margin-left: auto;
+`;
+
+
+export default class ReportLoader extends Component {
+  render() {
+    return <div id="loading-report" className='sweet-loading'>
+      <h1>Generating Report...</h1>
+      <GridLoader
+        css={loading_css_override}
+        sizeUnit={"px"}
+        size={20}
+        color={'#ffcc00'}
+        loading={this.props.loading}
+      />
+    </div>
+  }
+}
+
+ReportLoader.propTypes = {loading: PropTypes.bool};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js
new file mode 100644
index 000000000..41a45edad
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js
@@ -0,0 +1,22 @@
+import React, {Component, Fragment} from "react";
+import * as PropTypes from "prop-types";
+
+export default class SecurityIssuesGlance extends Component {
+  render() {
+    return <Fragment>
+      {
+        this.props.issuesFound ?
+          (<p className="alert alert-danger">
+            <i className="glyphicon glyphicon-exclamation-sign" style={{'marginRight': '5px'}}/>
+            Critical security issues were detected!
+          </p>) :
+          (<p className="alert alert-success">
+            <i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/>
+            No critical security issues were detected.
+          </p>)
+      }
+    </Fragment>
+  }
+}
+
+SecurityIssuesGlance.propTypes = {issuesFound: PropTypes.bool};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js
similarity index 75%
rename from monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js
index 10381fa5e..13f9cd92e 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js
@@ -2,34 +2,36 @@ import React from 'react';
 import {Col} from 'react-bootstrap';
 import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
 import {edgeGroupToColor, options} from 'components/map/MapOptions';
-import '../../styles/Collapse.scss';
-import AuthComponent from '../AuthComponent';
-import {ScanStatus} from "../attack/techniques/Helpers";
+import '../../../styles/Collapse.scss';
+import AuthComponent from '../../AuthComponent';
+import {ScanStatus} from "../../attack/techniques/Helpers";
 import Collapse from '@kunukn/react-collapse';
-import T1210 from '../attack/techniques/T1210';
-import T1197 from '../attack/techniques/T1197';
-import T1110 from '../attack/techniques/T1110';
-import T1075 from "../attack/techniques/T1075";
-import T1003 from "../attack/techniques/T1003";
-import T1059 from "../attack/techniques/T1059";
-import T1086 from "../attack/techniques/T1086";
-import T1082 from "../attack/techniques/T1082";
-import T1145 from "../attack/techniques/T1145";
-import T1105 from "../attack/techniques/T1105";
-import T1107 from "../attack/techniques/T1107";
-import T1065 from "../attack/techniques/T1065";
-import T1035 from "../attack/techniques/T1035";
-import T1129 from "../attack/techniques/T1129";
-import T1106 from "../attack/techniques/T1106";
-import T1188 from "../attack/techniques/T1188";
-import T1090 from "../attack/techniques/T1090";
-import T1041 from "../attack/techniques/T1041";
-import T1222 from "../attack/techniques/T1222";
-import T1005 from "../attack/techniques/T1005";
-import T1018 from "../attack/techniques/T1018";
-import T1016 from "../attack/techniques/T1016";
-import T1021 from "../attack/techniques/T1021";
-import T1064 from "../attack/techniques/T1064";
+
+import T1210 from '../../attack/techniques/T1210';
+import T1197 from '../../attack/techniques/T1197';
+import T1110 from '../../attack/techniques/T1110';
+import T1075 from "../../attack/techniques/T1075";
+import T1003 from "../../attack/techniques/T1003";
+import T1059 from "../../attack/techniques/T1059";
+import T1086 from "../../attack/techniques/T1086";
+import T1082 from "../../attack/techniques/T1082";
+import T1145 from "../../attack/techniques/T1145";
+import T1105 from "../../attack/techniques/T1105";
+import T1107 from "../../attack/techniques/T1107";
+import T1065 from "../../attack/techniques/T1065";
+import T1035 from "../../attack/techniques/T1035";
+import T1129 from "../../attack/techniques/T1129";
+import T1106 from "../../attack/techniques/T1106";
+import T1188 from "../../attack/techniques/T1188";
+import T1090 from "../../attack/techniques/T1090";
+import T1041 from "../../attack/techniques/T1041";
+import T1222 from "../../attack/techniques/T1222";
+import T1005 from "../../attack/techniques/T1005";
+import T1018 from "../../attack/techniques/T1018";
+import T1016 from "../../attack/techniques/T1016";
+import T1021 from "../../attack/techniques/T1021";
+import T1064 from "../../attack/techniques/T1064";
+import {extractExecutionStatusFromServerResponse} from "../common/ExecutionStatus";
 
 const tech_components = {
   'T1210': T1210,
@@ -80,11 +82,7 @@ class AttackReportPageComponent extends AuthComponent {
     return this.authFetch('/api')
       .then(res => res.json())
       .then(res => {
-        // This check is used to prevent unnecessary re-rendering
-        this.setState({
-          allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']),
-          runStarted: res['completed_steps']['run_monkey']
-        });
+        this.setState(extractExecutionStatusFromServerResponse(res));
         return res;
       });
   };
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js
similarity index 100%
rename from monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/CollapsibleWell.js
similarity index 100%
rename from monkey/monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/security/CollapsibleWell.js
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/PostBreach.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/PostBreach.js
similarity index 100%
rename from monkey/monkey_island/cc/ui/src/components/report-components/PostBreach.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/security/PostBreach.js
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/ScannedServers.js
similarity index 100%
rename from monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/security/ScannedServers.js
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js
similarity index 100%
rename from monkey/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/StrongUsers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StrongUsers.js
similarity index 100%
rename from monkey/monkey_island/cc/ui/src/components/report-components/StrongUsers.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/security/StrongUsers.js
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
new file mode 100644
index 000000000..761ff94a9
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
@@ -0,0 +1,43 @@
+import React, {Component, Fragment} from "react";
+import EventsModal from "./EventsModal";
+import {Badge, Button} from "react-bootstrap";
+import * as PropTypes from "prop-types";
+
+export default class EventsButton extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      isShow: false
+    }
+  }
+
+  hide = () => {
+    this.setState({isShow: false});
+  };
+
+  show = () => {
+    this.setState({isShow: true});
+  };
+
+  render() {
+    return <Fragment>
+        <EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide}
+                     exportFilename={this.props.exportFilename}/>
+        <div className="text-center" style={{"display": "grid"}}>
+          <Button className="btn btn-primary btn-lg" onClick={this.show}>
+            <i className="fa fa-list"/> Events {this.createEventsAmountBadge()}
+          </Button>
+        </div>
+    </Fragment>;
+  }
+
+  createEventsAmountBadge() {
+    const eventsAmountBadgeContent = this.props.events.length > 9 ? "9+" : this.props.events.length;
+    return <Badge>{eventsAmountBadgeContent}</Badge>;
+  }
+}
+
+EventsButton.propTypes = {
+  events: PropTypes.array,
+  exportFilename: PropTypes.string,
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js
new file mode 100644
index 000000000..a7f2fe41c
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js
@@ -0,0 +1,51 @@
+import React, {Component} from "react";
+import {Badge, Modal} from "react-bootstrap";
+import EventsTimeline from "./EventsTimeline";
+import * as PropTypes from "prop-types";
+import saveJsonToFile from "../../utils/SaveJsonToFile";
+import EventsModalButtons from "./EventsModalButtons";
+import Pluralize from 'pluralize'
+import {statusToLabelType} from "./StatusLabel";
+
+export default class EventsModal extends Component {
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    return (
+      <div>
+        <Modal show={this.props.showEvents} onHide={() => this.props.hideCallback()}>
+          <Modal.Body>
+            <h3>
+              <div className="text-center">Events</div>
+            </h3>
+            <hr />
+            <p>
+              There {Pluralize('is', this.props.events.length)} {<div className={"label label-primary"}>{this.props.events.length}</div>} {Pluralize('event', this.props.events.length)} associated with this finding.
+            </p>
+            {this.props.events.length > 5 ? this.renderButtons() : null}
+            <EventsTimeline events={this.props.events}/>
+            {this.renderButtons()}
+          </Modal.Body>
+        </Modal>
+      </div>
+    );
+  }
+
+  renderButtons() {
+    return <EventsModalButtons
+      onClickClose={() => this.props.hideCallback()}
+      onClickExport={() => {
+        const dataToSave = this.props.events;
+        const filename = this.props.exportFilename;
+        saveJsonToFile(dataToSave, filename);
+      }}/>;
+  }
+}
+
+EventsModal.propTypes = {
+  showEvents: PropTypes.bool,
+  events: PropTypes.array,
+  hideCallback: PropTypes.func,
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js
new file mode 100644
index 000000000..962c54893
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js
@@ -0,0 +1,20 @@
+import React, {Component} from "react";
+import ExportEventsButton from "./ExportEventsButton";
+import * as PropTypes from "prop-types";
+
+export default class EventsModalButtons extends Component {
+  render() {
+    return <div className="text-center">
+      <button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}}
+              onClick={this.props.onClickClose}>
+        Close
+      </button>
+      <ExportEventsButton onClick={this.props.onClickExport}/>
+    </div>
+  }
+}
+
+EventsModalButtons.propTypes = {
+  onClickClose: PropTypes.func,
+  onClickExport: PropTypes.func
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js
new file mode 100644
index 000000000..b7fb90811
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js
@@ -0,0 +1,36 @@
+import React, {Component} from "react";
+import {Timeline, TimelineEvent} from "react-event-timeline";
+import * as PropTypes from "prop-types";
+
+let monkeyLocalIcon = require('../../../images/zerotrust/im-alert-machine-icon.svg');
+let monkeyNetworkIcon = require('../../../images/zerotrust/im-alert-network-icon.svg');
+
+const eventTypeToIcon = {
+  "monkey_local": monkeyLocalIcon,
+  "monkey_network": monkeyNetworkIcon,
+};
+
+export default class EventsTimeline extends Component {
+  render() {
+    return (
+      <div>
+        <Timeline style={{fontSize: '100%'}}>
+          {
+            this.props.events.map((event, index) => {
+              const event_time = new Date(event.timestamp['$date']).toString();
+              return (<TimelineEvent
+                key={index}
+                createdAt={event_time}
+                title={event.title}
+                icon={<img src={eventTypeToIcon[event.event_type]} alt="icon" style={{width: '24px'}} />}>
+                  {event.message}
+              </TimelineEvent>)
+            })
+          }
+        </Timeline>
+      </div>
+    );
+  }
+}
+
+EventsTimeline.propTypes = {events: PropTypes.array};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js
new file mode 100644
index 000000000..bb6fc6c45
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js
@@ -0,0 +1,15 @@
+import React, {Component} from "react";
+import {Button} from "react-bootstrap";
+import * as PropTypes from "prop-types";
+
+export default class ExportEventsButton extends Component {
+  render() {
+    return <Button className="btn btn-primary btn-lg"
+                   onClick={this.props.onClick}
+    >
+      <i className="fa fa-download"/> Export
+    </Button>
+  }
+}
+
+ExportEventsButton.propTypes = {onClick: PropTypes.func};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js
new file mode 100644
index 000000000..95b9d0389
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js
@@ -0,0 +1,63 @@
+import React, {Component, Fragment} from "react";
+import PillarLabel from "./PillarLabel";
+import EventsButton from "./EventsButton";
+import ZeroTrustPillars, {ZeroTrustStatuses} from "./ZeroTrustPillars";
+import {FindingsTable} from "./FindingsTable";
+
+
+class FindingsSection extends Component {
+  mapFindingsByStatus() {
+    const statusToFindings = {};
+    for (const key in ZeroTrustStatuses) {
+      statusToFindings[ZeroTrustStatuses[key]] = [];
+    }
+
+    this.props.findings.map((finding) => {
+      // Deep copy
+      const newFinding = JSON.parse(JSON.stringify(finding));
+      newFinding.pillars = newFinding.pillars.map((pillar) => {
+        return {name: pillar, status: this.props.pillarsToStatuses[pillar]}
+      });
+      statusToFindings[newFinding.status].push(newFinding);
+    });
+    return statusToFindings;
+  }
+
+  render() {
+    const findingsByStatus = this.mapFindingsByStatus();
+    return (
+      <div id="findings-section">
+        <h2>Findings</h2>
+        <p>
+          Deep-dive into the details of each test, and see the explicit events and exact timestamps in which things
+          happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper
+          insight as to what exactly happened during this test.
+        </p>
+
+        <FindingsTable data={findingsByStatus[ZeroTrustStatuses.failed]} status={ZeroTrustStatuses.failed}/>
+        <FindingsTable data={findingsByStatus[ZeroTrustStatuses.verify]} status={ZeroTrustStatuses.verify}/>
+        <FindingsTable data={findingsByStatus[ZeroTrustStatuses.passed]} status={ZeroTrustStatuses.passed}/>
+      </div>
+    );
+  }
+
+  getFilteredFindings(statusToFilter) {
+    const findings = this.props.findings.map((finding) => {
+      // Deep copy
+      const newFinding = JSON.parse(JSON.stringify(finding));
+      if (newFinding.status === statusToFilter) {
+        newFinding.pillars = newFinding.pillars.map((pillar) => {
+          return {name: pillar, status: this.props.pillarsToStatuses[pillar]}
+        });
+        return newFinding;
+      }
+    });
+    // Filter nulls out of the list
+    return findings.filter(function (el) {
+      return el != null;
+    });
+  }
+}
+
+
+export default FindingsSection;
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js
new file mode 100644
index 000000000..acff1df89
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js
@@ -0,0 +1,53 @@
+import React, {Component, Fragment} from "react";
+import StatusLabel from "./StatusLabel";
+import PaginatedTable from "../common/PaginatedTable";
+import * as PropTypes from "prop-types";
+import PillarLabel from "./PillarLabel";
+import EventsButton from "./EventsButton";
+
+const EVENTS_COLUMN_MAX_WIDTH = 160;
+const PILLARS_COLUMN_MAX_WIDTH = 200;
+const columns = [
+  {
+    columns: [
+      {
+        Header: 'Finding', accessor: 'test',
+        style: {'whiteSpace': 'unset'}  // This enables word wrap
+      },
+
+      {
+        Header: 'Events', id: "events",
+        accessor: x => {
+          return <EventsButton events={x.events} exportFilename={"Events_" + x.test_key}/>;
+        },
+        maxWidth: EVENTS_COLUMN_MAX_WIDTH,
+      },
+
+      {
+        Header: 'Pillars', id: "pillars",
+        accessor: x => {
+          const pillars = x.pillars;
+          const pillarLabels = pillars.map((pillar) =>
+            <PillarLabel key={pillar.name} pillar={pillar.name} status={pillar.status}/>
+          );
+          return <div style={{textAlign: "center"}}>{pillarLabels}</div>;
+        },
+        maxWidth: PILLARS_COLUMN_MAX_WIDTH,
+        style: {'whiteSpace': 'unset'}
+      },
+    ]
+  }
+];
+
+
+export class FindingsTable extends Component {
+  render() {
+    return <Fragment>
+      <h3>{<span style={{display: "inline-block"}}><StatusLabel status={this.props.status} showText={true}/>
+      </span>} tests' findings</h3>
+      <PaginatedTable data={this.props.data} pageSize={10} columns={columns}/>
+    </Fragment>
+  }
+}
+
+FindingsTable.propTypes = {data: PropTypes.array, status: PropTypes.string};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js
new file mode 100644
index 000000000..51c5ca380
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js
@@ -0,0 +1,25 @@
+import React, {Component} from "react";
+import {statusToLabelType} from "./StatusLabel";
+import * as PropTypes from "prop-types";
+
+const pillarToIcon = {
+  "Data": "fa fa-database",
+  "People": "fa fa-user",
+  "Networks": "fa fa-wifi",
+  "Workloads": "fa fa-cloud",
+  "Devices": "fa fa-laptop",
+  "Visibility & Analytics": "fa fa-eye-slash",
+  "Automation & Orchestration": "fa fa-cogs",
+};
+
+export default class PillarLabel extends Component {
+  render() {
+    const className = "label " + statusToLabelType[this.props.status];
+    return <div className={className} style={{margin: '2px', display: 'inline-block'}}><i className={pillarToIcon[this.props.pillar]}/> {this.props.pillar}</div>
+  }
+}
+
+PillarLabel.propTypes = {
+  status: PropTypes.string,
+  pillar: PropTypes.string,
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js
new file mode 100644
index 000000000..7cefcab61
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js
@@ -0,0 +1,17 @@
+import React, {Component} from "react";
+import * as PropTypes from "prop-types";
+import ResponsiveVennDiagram from "./venn-components/ResponsiveVennDiagram";
+
+class PillarOverview extends Component {
+  render() {
+    return (<div id={this.constructor.name}>
+      <ResponsiveVennDiagram pillarsGrades={this.props.grades}/>
+    </div>);
+  }
+}
+
+export default PillarOverview;
+
+PillarOverview.propTypes = {
+  grades: PropTypes.array,
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js
new file mode 100644
index 000000000..bb957d42d
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js
@@ -0,0 +1,31 @@
+import React, {Component} from "react";
+import SinglePillarPrinciplesStatus from "./SinglePillarPrinciplesStatus";
+import * as PropTypes from "prop-types";
+
+export default class PrinciplesSection extends Component {
+  render() {
+    return <div id="principles-section">
+      <h2>Test Results</h2>
+      <p>
+        The <a href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210">
+        Zero Trust eXtended (ZTX) framework
+      </a> is composed of 7 pillars. Each pillar is built of
+        several guiding principles tested by the Infection Monkey.
+      </p>
+      {
+        Object.keys(this.props.principles).map((pillar) =>
+          <SinglePillarPrinciplesStatus
+            key={pillar}
+            pillar={pillar}
+            principlesStatus={this.props.principles[pillar]}
+            pillarsToStatuses={this.props.pillarsToStatuses}/>
+        )
+      }
+    </div>
+  }
+}
+
+PrinciplesSection.propTypes = {
+  principles: PropTypes.object,
+  pillarsToStatuses: PropTypes.object
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js
new file mode 100644
index 000000000..b50ee0c28
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js
@@ -0,0 +1,71 @@
+import React, {Fragment} from "react";
+import PaginatedTable from "../common/PaginatedTable";
+import AuthComponent from "../../AuthComponent";
+import StatusLabel from "./StatusLabel";
+import * as PropTypes from "prop-types";
+import {ZeroTrustStatuses} from "./ZeroTrustPillars";
+
+
+const MAX_WIDTH_STATUS_COLUMN = 80;
+const columns = [
+  {
+    columns: [
+      { Header: 'Status', id: 'status',
+        accessor: x => {
+          return <StatusLabel status={x.status} size="fa-3x" showText={false} />;
+        },
+        maxWidth: MAX_WIDTH_STATUS_COLUMN
+      },
+      { Header: 'Zero Trust Principle', accessor: 'principle',
+        style: {'whiteSpace': 'unset'}  // This enables word wrap
+      },
+      { Header: 'Monkey Tests', id: 'tests',
+        style: {'whiteSpace': 'unset'},  // This enables word wrap
+        accessor: x => {
+          return <TestsStatus tests={x.tests} />;
+        }
+      }
+    ]
+  }
+];
+
+class TestsStatus extends AuthComponent {
+  render() {
+    return (
+      <Fragment>
+        {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.failed)}
+        {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.verify)}
+        {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.passed)}
+        {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.unexecuted)}
+      </Fragment>
+    );
+  }
+
+  getFilteredTestsByStatusIfAny(statusToFilter) {
+    const filteredTests = this.props.tests.filter((test) => {
+        return (test.status === statusToFilter);
+      }
+    );
+
+    if (filteredTests.length > 0) {
+      const listItems = filteredTests.map((test) => {
+        return (<li key={test.test}>{test.test}</li>)
+      });
+      return <Fragment>
+        <StatusLabel status={statusToFilter} showText={false}/>
+        <ul>{listItems}</ul>
+      </Fragment>;
+    }
+    return <Fragment/>;
+  }
+}
+
+export class PrinciplesStatusTable extends AuthComponent {
+  render() {
+    return <PaginatedTable data={this.props.principlesStatus} columns={columns} pageSize={5}/>;
+  }
+}
+
+export default PrinciplesStatusTable;
+
+PrinciplesStatusTable.propTypes = {principlesStatus: PropTypes.array};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
new file mode 100644
index 000000000..5ef75f2b4
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
@@ -0,0 +1,60 @@
+import React, {Component} from "react";
+import StatusLabel from "./StatusLabel";
+import {ZeroTrustStatuses} from "./ZeroTrustPillars";
+import {NavLink} from "react-router-dom";
+import {Panel} from "react-bootstrap";
+
+
+class ZeroTrustReportLegend extends Component {
+  render() {
+    const legendContent = this.getLegendContent();
+
+    return (
+      <Panel>
+        <Panel.Heading>
+          <Panel.Title toggle>
+            <h3><i className="fa fa-chevron-down" /> Legend</h3>
+          </Panel.Title>
+        </Panel.Heading>
+        <Panel.Collapse>
+          <Panel.Body>
+            {legendContent}
+          </Panel.Body>
+        </Panel.Collapse>
+      </Panel>
+    );
+  }
+
+  getLegendContent() {
+    return <div id={this.constructor.name}>
+      <ul style={{listStyle: "none"}}>
+        <li>
+          <div style={{display: "inline-block"}}>
+            <StatusLabel showText={true} status={ZeroTrustStatuses.failed}/>
+          </div>
+          {"\t"}At least one of the tests related to this component failed. This means that the Infection Monkey detected an unmet Zero Trust requirement.
+        </li>
+        <li>
+          <div style={{display: "inline-block"}}>
+            <StatusLabel showText={true} status={ZeroTrustStatuses.verify}/>
+          </div>
+          {"\t"}At least one of the tests’ results related to this component requires further manual verification.
+        </li>
+        <li>
+          <div style={{display: "inline-block"}}>
+            <StatusLabel showText={true} status={ZeroTrustStatuses.passed}/>
+          </div>
+          {"\t"}All Tests related to this pillar passed. No violation of a Zero Trust guiding principle was detected.
+        </li>
+        <li>
+          <div style={{display: "inline-block"}}>
+            <StatusLabel showText={true} status={ZeroTrustStatuses.unexecuted}/>
+          </div>
+          {"\t"}This status means the test wasn't executed.To activate more tests, refer to the Monkey <NavLink to="/configuration"><u>configuration</u></NavLink> page.
+        </li>
+      </ul>
+    </div>;
+  }
+}
+
+export default ZeroTrustReportLegend;
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js
new file mode 100644
index 000000000..8e4512ac7
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js
@@ -0,0 +1,37 @@
+import AuthComponent from "../../AuthComponent";
+import PillarLabel from "./PillarLabel";
+import PrinciplesStatusTable from "./PrinciplesStatusTable";
+import React from "react";
+import * as PropTypes from "prop-types";
+import {Panel} from "react-bootstrap";
+
+export default class SinglePillarPrinciplesStatus extends AuthComponent {
+  render() {
+    if (this.props.principlesStatus.length === 0) {
+      return null;
+    }
+    else {
+      return (
+        <Panel>
+          <Panel.Heading>
+            <Panel.Title toggle>
+              <h3 style={{textAlign: "center", marginTop: "1px", marginBottom: "1px"}}>
+                <i className="fa fa-chevron-down" /> <PillarLabel pillar={this.props.pillar} status={this.props.pillarsToStatuses[this.props.pillar]} />
+              </h3>
+            </Panel.Title>
+          </Panel.Heading>
+          <Panel.Collapse>
+            <Panel.Body>
+              <PrinciplesStatusTable principlesStatus={this.props.principlesStatus}/>
+            </Panel.Body>
+          </Panel.Collapse>
+        </Panel>
+      );
+    }
+  }
+}
+
+SinglePillarPrinciplesStatus.propTypes = {
+  principlesStatus: PropTypes.array,
+  pillar: PropTypes.string,
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js
new file mode 100644
index 000000000..028ca7d89
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js
@@ -0,0 +1,37 @@
+import React, {Component} from "react";
+import * as PropTypes from "prop-types";
+
+const statusToIcon = {
+  "Passed": "fa-check",
+  "Verify": "fa-exclamation-triangle",
+  "Failed": "fa-bomb",
+  "Unexecuted": "fa-question",
+};
+
+export const statusToLabelType = {
+  "Passed": "label-success",
+  "Verify": "label-warning",
+  "Failed": "label-danger",
+  "Unexecuted": "label-default",
+};
+
+export default class StatusLabel extends Component {
+  render() {
+    let text = "";
+    if (this.props.showText) {
+      text = " " + this.props.status;
+    }
+
+    return (
+      <div className={"label " + statusToLabelType[this.props.status]} style={{display: "flow-root"}}>
+        <i className={"fa " + statusToIcon[this.props.status] + " " + this.props.size}/>{text}
+      </div>
+    );
+  }
+}
+
+StatusLabel.propTypes = {
+  status: PropTypes.string,
+  showText: PropTypes.bool,
+  size: PropTypes.string
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js
new file mode 100644
index 000000000..d34a484b9
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js
@@ -0,0 +1,37 @@
+import React, {Component, Fragment} from "react";
+import PillarLabel from "./PillarLabel";
+import StatusLabel from "./StatusLabel";
+import * as PropTypes from "prop-types";
+import {ZeroTrustStatuses} from "./ZeroTrustPillars";
+
+export default class StatusesToPillarsSummary extends Component {
+  render() {
+    return (<div id="piilar-summary">
+      {this.getStatusSummary(ZeroTrustStatuses.failed)}
+      {this.getStatusSummary(ZeroTrustStatuses.verify)}
+      {this.getStatusSummary(ZeroTrustStatuses.passed)}
+      {this.getStatusSummary(ZeroTrustStatuses.unexecuted)}
+    </div>);
+  }
+
+  getStatusSummary(status) {
+    if (this.props.statusesToPillars[status].length > 0) {
+      return <Fragment>
+        <h3>
+          <StatusLabel showText={true} status={status}/>
+        </h3>
+        <div>
+            {
+              this.props.statusesToPillars[status].map((pillar) => {
+                return <PillarLabel key={pillar} pillar={pillar} status={status} />
+              })
+            }
+        </div>
+      </Fragment>
+    }
+  }
+}
+
+StatusesToPillarsSummary.propTypes = {
+  statusesToPillars: PropTypes.object
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js
new file mode 100644
index 000000000..e4012bf50
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js
@@ -0,0 +1,40 @@
+import React, {Component} from "react";
+import {Col, Grid, Row} from "react-bootstrap";
+import MonkeysStillAliveWarning from "../common/MonkeysStillAliveWarning";
+import PillarsOverview from "./PillarOverview";
+import ZeroTrustReportLegend from "./ReportLegend";
+import * as PropTypes from "prop-types";
+
+export default class SummarySection extends Component {
+  render() {
+    return <div id="summary-section">
+      <h2>Summary</h2>
+      <Grid fluid={true}>
+        <Row>
+          <Col xs={12} sm={12} md={12} lg={12}>
+            <MonkeysStillAliveWarning allMonkeysAreDead={this.props.allMonkeysAreDead}/>
+            <p>
+              Get a quick glance at how your network aligns with the <a href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210">
+                Zero Trust eXtended (ZTX) framework
+              </a>.
+            </p>
+          </Col>
+        </Row>
+        <Row className="show-grid">
+          <Col xs={8} sm={8} md={8} lg={8}>
+            <PillarsOverview pillarsToStatuses={this.props.pillars.pillarsToStatuses}
+                             grades={this.props.pillars.grades}/>
+          </Col>
+          <Col xs={4} sm={4} md={4} lg={4}>
+            <ZeroTrustReportLegend/>
+          </Col>
+        </Row>
+      </Grid>
+    </div>
+  }
+}
+
+SummarySection.propTypes = {
+  allMonkeysAreDead: PropTypes.bool,
+  pillars: PropTypes.object
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js
new file mode 100644
index 000000000..dd2a55865
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js
@@ -0,0 +1,18 @@
+export const ZeroTrustPillars = {
+  data: "Data",
+  people: "People",
+  network: "Networks",
+  workload: "Workload",
+  devices: "Devices",
+  visibility: "Visibility & Analytics",
+  automation: "Automation & Orchestration"
+};
+
+export const ZeroTrustStatuses = {
+  failed: "Failed",
+  verify: "Verify",
+  passed: "Passed",
+  unexecuted: "Unexecuted"
+};
+
+export default ZeroTrustPillars;
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/.DS_Store b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/.DS_Store
new file mode 100644
index 000000000..344923cf9
Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/.DS_Store differ
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js
new file mode 100644
index 000000000..aee1fb7f2
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js
@@ -0,0 +1,65 @@
+import React from 'react'
+import PropTypes from 'prop-types';
+import {Popover, OverlayTrigger} from 'react-bootstrap';
+import * as d3 from 'd3'
+
+class ArcNode extends React.Component {
+  render() {
+    let {prefix, index, data} = this.props;
+
+    let arc = d3.arc().innerRadius(data.inner).outerRadius(data.outer).startAngle(0).endAngle(Math.PI * 2.0);
+    let id = prefix + 'Node_' + index;
+
+    return (
+      <g transform={'rotate(180)'} id={data.node.pillar} key={prefix + 'arcGroup' + index}>
+        <OverlayTrigger ref={'overlay'} key={prefix + 'arcOverlayTrigger' + index} trigger={null}
+                        placement={data.popover}
+                        overlay={<Popover id={prefix + 'ArcPopover' + index} style={{backgroundColor: data.hex}}
+                                          title={data.node.pillar}>{data.tooltip}</Popover>} rootClose>
+          <path
+
+            id={prefix + 'Node_' + index}
+            className={'arcNode'}
+            data-tooltip={data.tooltip}
+            d={arc()}
+            fill={data.hex}
+            onClick={this.handleClick.bind(this)}
+            onMouseEnter={this.handleOver.bind(this)}
+            onMouseLeave={this.handleOut.bind(this)}
+
+          />
+        </OverlayTrigger>
+        <text x={0} dy={data.fontStyle.size * 1.2} fontSize={data.fontStyle.size} fill={'white'} textAnchor='middle'
+              pointerEvents={'none'}>
+          <textPath href={'#' + id} startOffset={'26.4%'}>
+            <tspan fontFamily={'FontAwesome'}>{data.icon + '\u2000'}</tspan>
+            <tspan>{data.label}</tspan>
+          </textPath>
+        </text>
+      </g>
+    );
+  }
+
+
+  handleClick(e_) {
+    this.props.disableHover(this.refs.overlay);
+  }
+
+  handleOver(e_) {
+    if (this.props.hover) {
+      this.refs.overlay.show();
+    }
+  }
+
+  handleOut(e_) {
+    if (this.props.hover) {
+      this.refs.overlay.hide();
+    }
+  }
+}
+
+ArcNode.propTypes = {
+  data: PropTypes.object
+};
+
+export default ArcNode;
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js
new file mode 100644
index 000000000..5c84d95a5
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js
@@ -0,0 +1,64 @@
+import React from 'react'
+import PillarLabel from "../PillarLabel";
+import {Popover, OverlayTrigger} from 'react-bootstrap';
+import PropTypes from 'prop-types';
+
+class CircularNode extends React.Component {
+  render() {
+    let {prefix, index, data} = this.props;
+
+    let translate = 'translate(' + data.cx + ',' + data.cy + ')';
+    return (
+      <g transform={translate} id={data.node.pillar} key={prefix + 'circularGroup' + index}>
+        <OverlayTrigger ref={'overlay'} key={prefix + 'CircularOverlay' + index} trigger={null} placement={data.popover}
+                        overlay={<Popover id={prefix + 'CircularClickPopover' + index}
+                                          style={{backgroundColor: data.hex}}
+                                          title={data.node.pillar}>{data.tooltip}</Popover>} rootClose>
+          <circle
+            id={prefix + 'Node_' + index}
+            className={'circularNode'}
+            data-tooltip={data.tooltip}
+            r={data.r}
+            opacity={0.8}
+            fill={data.hex}
+            onClick={this.handleClick.bind(this)}
+            onMouseEnter={this.handleOver.bind(this)}
+            onMouseLeave={this.handleOut.bind(this)}
+
+          />
+        </OverlayTrigger>
+        <foreignObject style={{fontSize: data.fontStyle.size, pointerEvents: 'none'}}
+                       key={prefix + 'PillarLabelOject' + index} x={data.offset.x - data.fontStyle.size * 6}
+                       y={data.offset.y - data.fontStyle.size} width={data.fontStyle.size * 12}
+                       height={data.fontStyle.size * 6}>
+          <PillarLabel key={prefix + 'PillarLabel' + index} pillar={data.node.pillar} status={data.status}/>
+        </foreignObject>
+      </g>
+    );
+  }
+
+
+  handleClick(e_) {
+    this.props.disableHover(this.refs.overlay);
+  }
+
+  handleOver(e_) {
+    if (this.props.hover) {
+      this.refs.overlay.show();
+    }
+  }
+
+  handleOut(e_) {
+    if (this.props.hover) {
+      this.refs.overlay.hide();
+    }
+  }
+
+}
+
+CircularNode.propTypes = {
+  index: PropTypes.number,
+  data: PropTypes.object
+};
+
+export default CircularNode;
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js
new file mode 100644
index 000000000..4b2069f06
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js
@@ -0,0 +1,28 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import Dimensions from 'react-dimensions'
+import VennDiagram from './VennDiagram'
+
+const VENN_MIN_WIDTH = '300px';
+
+class ResponsiveVennDiagram extends React.Component {
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    const {pillarsGrades} = this.props;
+
+    return (
+      <div ref={this.divElement} style={{textAlign: 'center'}}>
+        <VennDiagram style={{minWidth: VENN_MIN_WIDTH, minHeight: VENN_MIN_WIDTH}} pillarsGrades={pillarsGrades}/>
+      </div>
+    );
+  }
+}
+
+ResponsiveVennDiagram.propTypes = {
+  pillarsGrades: PropTypes.array
+};
+
+export default Dimensions()(ResponsiveVennDiagram);
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js
new file mode 100644
index 000000000..fa9309506
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js
@@ -0,0 +1,9 @@
+export class TypographicUtilities {
+  static removeAmpersand(string_) {
+    return string_.replace(' & ', 'And');
+  }
+
+  static removeBrokenBar(string_) {
+    return string_.replace(/\|/g, ' ');
+  }
+}
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css
new file mode 100644
index 000000000..dd4883125
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css
@@ -0,0 +1,17 @@
+@import url('https://fonts.googleapis.com/css?family=Noto+Sans&display=swap');
+
+body {
+  margin: 0;
+  font-family: "Noto Sans", sans-serif;
+}
+
+svg {
+
+  -webkit-touch-callout: none; /* iOS Safari */
+  -webkit-user-select: none; /* Safari */
+  -khtml-user-select: none; /* Konqueror HTML */
+  -moz-user-select: none; /* Firefox */
+  -ms-user-select: none; /* Internet Explorer/Edge */
+  user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
+
+}
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js
new file mode 100644
index 000000000..70304daad
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js
@@ -0,0 +1,280 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import CircularNode from './CircularNode'
+import ArcNode from './ArcNode'
+import {TypographicUtilities} from './Utility.js'
+import './VennDiagram.css'
+import {ZeroTrustStatuses} from "../ZeroTrustPillars";
+
+class VennDiagram extends React.Component {
+  constructor(props_) {
+    super(props_);
+
+    this.state = {hover: true, currentPopover: undefined};
+    this._disableHover = this._disableHover.bind(this);
+
+    this.width = this.height = 512;
+
+    this.prefix = 'vennDiagram';
+    this.fontStyles = [{size: Math.max(9, this.width / 28), color: 'white'}, {
+      size: Math.max(6, this.width / 38),
+      color: 'white'
+    }, {size: Math.max(6, this.width / 48), color: 'white'}];
+    this.offset = this.width / 16;
+
+    this.thirdWidth = this.width / 3;
+    this.width11By2 = this.width / 5.5;
+    this.width2By7 = 2 * this.width / 7;
+    this.width1By11 = this.width / 11;
+    this.width1By28 = this.width / 28;
+    this.arcNodesGap = 4;
+
+    this.layout = {
+      Data: {cx: 0, cy: 0, r: this.width11By2, offset: {x: 0, y: 0}, popover: 'top'},
+      People: {
+        cx: -this.width2By7,
+        cy: 0,
+        r: this.width11By2,
+        offset: {x: this.width1By11 + this.fontStyles[1].size / 5 * 3, y: 0},
+        popover: 'right'
+      },
+      Networks: {
+        cx: this.width2By7,
+        cy: 0,
+        r: this.width11By2,
+        offset: {x: -this.width1By11 - this.fontStyles[1].size / 5 * 3, y: 0},
+        popover: 'left'
+      },
+      Devices: {
+        cx: 0,
+        cy: this.width2By7,
+        r: this.width11By2,
+        offset: {x: 0, y: -this.width1By11 + this.fontStyles[1].size / 6 * 3},
+        popover: 'top'
+      },
+      Workloads: {
+        cx: 0,
+        cy: -this.width2By7,
+        r: this.width11By2,
+        offset: {x: 0, y: this.width1By11},
+        popover: 'bottom'
+      },
+      VisibilityAndAnalytics: {
+        inner: this.thirdWidth - this.width1By28,
+        outer: this.thirdWidth,
+        icon: '\uf070',
+        popover: 'right'
+      },
+      AutomationAndOrchestration: {
+        inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap,
+        outer: this.thirdWidth - this.width1By28 - this.arcNodesGap,
+        icon: '\uf085',
+        popover: 'right'
+      }
+    };
+
+    /*
+
+    RULE #1: All scores have to be equal 0, except Unexecuted [U] which could be also a negative integer
+             sum(C, I, P) has to be <=0
+
+    RULE #2: Failed [C] has to be > 0,
+             sum(C) > 0
+
+    RULE #3: Verify [I] has to be > 0 while Failed has to be 0,
+             sum(C, I) > 0 and C * I = 0, while C has to be 0
+
+    RULE #4: By process of elimination, passed.
+             if the P is bigger by 2 then negative U, first conditional
+             would be true.
+    */
+
+    this.rules = [
+
+      {
+        id: 'Rule #1', status: ZeroTrustStatuses.unexecuted, hex: '#777777', f: function (d_) {
+          return d_[ZeroTrustStatuses.failed] + d_[ZeroTrustStatuses.verify] + d_[ZeroTrustStatuses.passed] === 0;
+        }
+      },
+      {
+        id: 'Rule #2', status: ZeroTrustStatuses.failed, hex: '#D9534F', f: function (d_) {
+          return d_[ZeroTrustStatuses.failed] > 0;
+        }
+      },
+      {
+        id: 'Rule #3', status: ZeroTrustStatuses.verify, hex: '#F0AD4E', f: function (d_) {
+          return d_[ZeroTrustStatuses.failed] === 0 && d_[ZeroTrustStatuses.verify] > 0;
+        }
+      },
+      {
+        id: 'Rule #4', status: ZeroTrustStatuses.passed, hex: '#5CB85C', f: function (d_) {
+          return d_[ZeroTrustStatuses.passed] > 0;
+        }
+      }
+
+    ];
+
+  }
+
+  componentDidMount() {
+    this.parseData();
+    if (this.state.currentPopover !== undefined) {
+      this.state.currentPopover.show();
+    }
+  }
+
+  _disableHover(ref_) {
+    this.setState({hover: false, currentPopover: ref_, data: this.state.data});
+  }
+
+  _onMouseMove(e) {
+
+    let self = this;
+
+    let hidden = 'none';
+    let html = '';
+    let bcolor = '#DEDEDE';
+
+    if (this.state.currentPopover !== undefined) {
+      this.state.currentPopover.show();
+    }
+
+    document.querySelectorAll('circle, path').forEach((d_, i_) => {
+      d_.setAttribute('opacity', "0.8");
+    });
+
+    if (e.target.id.includes('Node')) {
+
+      e.target.setAttribute('opacity', 0.95);
+
+      // Set highest z-index
+      e.target.parentNode.parentNode.appendChild(e.target.parentNode);
+
+    } else {
+
+      // Return z indices to default
+      Object.keys(this.layout).forEach(function (d_, i_) {
+        document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode);
+      })
+    }
+
+  }
+
+  _onClick(e) {
+
+    if (!e.target.id.includes('Node')) {
+
+      this.state.currentPopover.hide();
+      this.setState({hover: true, currentPopover: undefined, data: this.state.data});
+    }
+  }
+
+  parseData() {
+
+    let self = this;
+    let data = [];
+    const omit = (prop, {[prop]: _, ...rest}) => rest;
+
+    this.props.pillarsGrades.forEach((d_, i_) => {
+
+      let params = omit('pillar', d_);
+      let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_] || 0), 0);
+      let key = TypographicUtilities.removeAmpersand(d_.pillar);
+      let html = self.buildTooltipHtmlContent(params);
+      let rule = null;
+
+      for (let j = 0; j < self.rules.length; j++) {
+        if (self.rules[j].f(d_)) {
+          rule = j;
+          break;
+        }
+      }
+
+      self.setLayoutElement(rule, key, html, d_);
+      data.push(this.layout[key]);
+
+    });
+
+    this.setState({hover: true, activePopover: undefined, data: data});
+    this.render();
+  }
+
+  buildTooltipHtmlContent(object_) {
+
+    return Object.keys(object_).map((key_, i_) => {
+      return (<p key={this.prefix + key_ + i_}>{key_}: {object_[key_]}</p>)
+    })
+  }
+
+  setLayoutElement(rule_, key_, html_, d_) {
+
+    if (rule_ === null) {
+      console.log(Error('The node scores are invalid, please check the data or the rules set.'));
+    }
+
+    if (key_ === 'Data') {
+      this.layout[key_].fontStyle = this.fontStyles[0];
+    } else if (this.layout[key_].hasOwnProperty('cx')) {
+      this.layout[key_].fontStyle = this.fontStyles[1];
+    } else {
+      this.layout[key_].fontStyle = this.fontStyles[2];
+    }
+
+    this.layout[key_].hex = this.rules[rule_].hex;
+    this.layout[key_].status = this.rules[rule_].status;
+    this.layout[key_].label = d_.pillar;
+    this.layout[key_].node = d_;
+    this.layout[key_].tooltip = html_;
+  }
+
+  render() {
+    if (this.state.data === undefined) {
+      return null;
+    } else {
+      // equivalent to center translate (width/2, height/2)
+      let viewPortParameters = (-this.width / 2) + ' ' + (-this.height / 2) + ' ' + this.width + ' ' + this.height;
+      let nodes = Object.values(this.layout).map((d_, i_) => {
+        if (d_.hasOwnProperty('cx')) {
+          return (
+            <CircularNode
+              prefix={this.prefix}
+              key={this.prefix + 'CircularNode' + i_}
+              index={i_}
+              data={d_}
+              hover={this.state.hover}
+              disableHover={this._disableHover}
+            />
+          );
+        } else {
+          d_.label = TypographicUtilities.removeBrokenBar(d_.label);
+          return (
+            <ArcNode
+              prefix={this.prefix}
+              key={this.prefix + 'ArcNode' + i_}
+              index={i_}
+              data={d_}
+              hover={this.state.hover}
+              disableHover={this._disableHover}
+            />
+          );
+        }
+      });
+
+      return (
+        <div ref={(divElement) => this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)}
+             onClick={this._onClick.bind(this)}>
+          <svg id={this.prefix} viewBox={viewPortParameters} width={'100%'} height={'100%'}
+               xmlns='http://www.w3.org/2000/svg' xmlnsXlink='http://www.w3.org/1999/xlink'>
+            {nodes}
+          </svg>
+        </div>
+      )
+    }
+  }
+}
+
+VennDiagram.propTypes = {
+  pillarsGrades: PropTypes.array
+};
+
+export default VennDiagram;
diff --git a/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js b/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js
new file mode 100644
index 000000000..6ad124457
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js
@@ -0,0 +1,7 @@
+import FileSaver from "file-saver";
+
+export default function saveJsonToFile(dataToSave, filename) {
+  const content = JSON.stringify(dataToSave, null, 2);
+  const blob = new Blob([content], {type: "text/plain;charset=utf-8"});
+  FileSaver.saveAs(blob, filename + ".json");
+}
diff --git a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg
new file mode 100644
index 000000000..507541be4
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44"><title>im-alert-machine-icon</title><g id="Layer_2" data-name="Layer 2"><g id="originals"><circle cx="22" cy="22" r="22" style="fill:#fc2"/><rect x="10" y="14" width="24" height="16.20144" style="fill:none;stroke:#333;stroke-miterlimit:10;stroke-width:1.9409949287049788px"/><rect x="21" y="17" width="2" height="6" style="fill:#333"/><rect x="21" y="25" width="2" height="2" style="fill:#333"/><rect x="18" y="31" width="8" height="3" style="fill:#333"/></g></g></svg>
\ No newline at end of file
diff --git a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg
new file mode 100644
index 000000000..50dcc6726
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44"><title>im-alert-network-icon</title><g id="Layer_2" data-name="Layer 2"><g id="originals"><circle cx="22" cy="22" r="22" style="fill:#fc2"/><circle cx="16.5" cy="7.5" r="2.5" style="fill:#333"/><circle cx="6.5" cy="23.5" r="2.5" style="fill:#333"/><circle cx="11" cy="32" r="2" style="fill:#333"/><circle cx="21.5" cy="37.5" r="2.49988" style="fill:#333"/><circle cx="35" cy="30" r="2" style="fill:#333"/><circle cx="33" cy="12" r="2" style="fill:#333"/><line x1="35" y1="30" x2="22" y2="22" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="21.5" y1="38" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="11.5" y1="31.5" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="21.5" y1="22.5" x2="6.5" y2="23.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="16.5" y1="7.5" x2="22.5" y2="23.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="33.5" y1="11.5" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><circle cx="22" cy="22" r="9" style="fill:#fc2;stroke:#333;stroke-miterlimit:10;stroke-width:2px"/><rect x="21" y="17" width="2" height="6" style="fill:#333"/><rect x="21" y="25" width="2" height="2" style="fill:#333"/></g></g></svg>
\ No newline at end of file
diff --git a/monkey/monkey_island/cc/ui/src/styles/App.css b/monkey/monkey_island/cc/ui/src/styles/App.css
index 8f602fa08..109f1c147 100644
--- a/monkey/monkey_island/cc/ui/src/styles/App.css
+++ b/monkey/monkey_island/cc/ui/src/styles/App.css
@@ -549,6 +549,22 @@ body {
   color: #e0ddde !important;
 }
 
+.status-success {
+  color: #24b716 !important;
+}
+
+.status-warning {
+  color: #b1a91c !important;
+}
+
+.status-danger {
+  color: #d91016 !important;
+}
+
+.status-default {
+  color: #575556 !important;
+}
+
 .attack-legend {
   text-align: center;
   margin-bottom: 20px;