From 026cb41064150191609b05805dd6d24a06a13b3c Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Thu, 30 May 2019 09:17:24 +0300
Subject: [PATCH 1/4] WIP - initial code, just trying to make a POST request
 work

---
 monkey/monkey_island/cc/resources/netmap.py   | 31 +++++++++++++++++++
 .../cc/ui/src/components/pages/ReportPage.js  | 12 ++++++-
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py
index ed83414f5..96ea38b28 100644
--- a/monkey/monkey_island/cc/resources/netmap.py
+++ b/monkey/monkey_island/cc/resources/netmap.py
@@ -1,4 +1,7 @@
+import json
+
 import flask_restful
+from flask import request
 
 from monkey_island.cc.auth import jwt_required
 from monkey_island.cc.services.edge import EdgeService
@@ -28,4 +31,32 @@ class NetMap(flask_restful.Resource):
                 "edges": edges
             }
 
+    @jwt_required()
+    def post(self, **kw):
+        post_data = json.loads(request.data)
 
+        print(post_data)
+
+        monkeys = [NodeService.monkey_to_net_node(x) for x in mongo.db.monkey.find({})]
+        nodes = [NodeService.node_to_net_node(x) for x in mongo.db.node.find({})]
+        edges = [EdgeService.edge_to_net_edge(x) for x in mongo.db.edge.find({})]
+
+        if NodeService.get_monkey_island_monkey() is None:
+            monkey_island = [NodeService.get_monkey_island_pseudo_net_node()]
+            edges += EdgeService.get_monkey_island_pseudo_edges()
+        else:
+            monkey_island = []
+            edges += EdgeService.get_infected_monkey_island_pseudo_edges()
+
+        all_nodes = monkeys + nodes + monkey_island
+        def filter_linux(machine):
+            if machine["os"] == "linux":
+                return False
+            return True
+        all_nodes = filter(filter_linux, all_nodes)
+
+        return \
+            {
+                "nodes": all_nodes,
+                "edges": edges
+            }
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 eb94792ab..65a8b9003 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -103,7 +103,17 @@ class ReportPageComponent extends AuthComponent {
   };
 
   updateMapFromServer = () => {
-    this.authFetch('/api/netmap')
+    this.authFetch('/api/netmap', {
+        method: 'POST',
+        headers: {
+          'Accept': 'application/json',
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({
+          firstParam: 'yourValue',
+          secondParam: 'yourOtherValue',
+        })
+    })
       .then(res => res.json())
       .then(res => {
         res.edges.forEach(edge => {

From bf0b812f7330f8ae4f518d2de720e7598e419c63 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 5 Jun 2019 17:18:52 +0300
Subject: [PATCH 2/4] Revert "WIP - initial code, just trying to make a POST
 request work"

This reverts commit 026cb41064150191609b05805dd6d24a06a13b3c.
---
 monkey/monkey_island/cc/resources/netmap.py   | 31 -------------------
 .../cc/ui/src/components/pages/ReportPage.js  | 12 +------
 2 files changed, 1 insertion(+), 42 deletions(-)

diff --git a/monkey/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py
index 96ea38b28..ed83414f5 100644
--- a/monkey/monkey_island/cc/resources/netmap.py
+++ b/monkey/monkey_island/cc/resources/netmap.py
@@ -1,7 +1,4 @@
-import json
-
 import flask_restful
-from flask import request
 
 from monkey_island.cc.auth import jwt_required
 from monkey_island.cc.services.edge import EdgeService
@@ -31,32 +28,4 @@ class NetMap(flask_restful.Resource):
                 "edges": edges
             }
 
-    @jwt_required()
-    def post(self, **kw):
-        post_data = json.loads(request.data)
 
-        print(post_data)
-
-        monkeys = [NodeService.monkey_to_net_node(x) for x in mongo.db.monkey.find({})]
-        nodes = [NodeService.node_to_net_node(x) for x in mongo.db.node.find({})]
-        edges = [EdgeService.edge_to_net_edge(x) for x in mongo.db.edge.find({})]
-
-        if NodeService.get_monkey_island_monkey() is None:
-            monkey_island = [NodeService.get_monkey_island_pseudo_net_node()]
-            edges += EdgeService.get_monkey_island_pseudo_edges()
-        else:
-            monkey_island = []
-            edges += EdgeService.get_infected_monkey_island_pseudo_edges()
-
-        all_nodes = monkeys + nodes + monkey_island
-        def filter_linux(machine):
-            if machine["os"] == "linux":
-                return False
-            return True
-        all_nodes = filter(filter_linux, all_nodes)
-
-        return \
-            {
-                "nodes": all_nodes,
-                "edges": edges
-            }
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 65a8b9003..eb94792ab 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -103,17 +103,7 @@ class ReportPageComponent extends AuthComponent {
   };
 
   updateMapFromServer = () => {
-    this.authFetch('/api/netmap', {
-        method: 'POST',
-        headers: {
-          'Accept': 'application/json',
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          firstParam: 'yourValue',
-          secondParam: 'yourOtherValue',
-        })
-    })
+    this.authFetch('/api/netmap')
       .then(res => res.json())
       .then(res => {
         res.edges.forEach(edge => {

From 833af00421ea91b168cdb931564b79375b5cabcc Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 5 Jun 2019 18:28:00 +0300
Subject: [PATCH 3/4] Added the IslandTestCase class for better testing + moved
 get_os to the monkey model

---
 monkey/monkey_island/cc/models/monkey.py      |  8 +++++
 monkey/monkey_island/cc/models/test_monkey.py | 29 +++++++++++++++++--
 .../cc/testing/IslandTestCase.py              | 12 ++++++++
 monkey/monkey_island/cc/testing/__init__.py   |  0
 4 files changed, 47 insertions(+), 2 deletions(-)
 create mode 100644 monkey/monkey_island/cc/testing/IslandTestCase.py
 create mode 100644 monkey/monkey_island/cc/testing/__init__.py

diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py
index bb018caa9..3e1e3d7c5 100644
--- a/monkey/monkey_island/cc/models/monkey.py
+++ b/monkey/monkey_island/cc/models/monkey.py
@@ -55,6 +55,14 @@ class Monkey(Document):
                 monkey_is_dead = True
         return monkey_is_dead
 
+    def get_os(self):
+        os = "unknown"
+        if self.description.lower().find("linux") != -1:
+            os = "linux"
+        elif self.description.lower().find("windows") != -1:
+            os = "windows"
+        return os
+
 
 class MonkeyNotFoundError(Exception):
     pass
diff --git a/monkey/monkey_island/cc/models/test_monkey.py b/monkey/monkey_island/cc/models/test_monkey.py
index 008fb0ce6..a744db6b6 100644
--- a/monkey/monkey_island/cc/models/test_monkey.py
+++ b/monkey/monkey_island/cc/models/test_monkey.py
@@ -1,13 +1,13 @@
 import uuid
 from time import sleep
-from unittest import TestCase
 
 from monkey import Monkey
 from monkey_island.cc.models.monkey import MonkeyNotFoundError
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
 from monkey_ttl import MonkeyTtl
 
 
-class TestMonkey(TestCase):
+class TestMonkey(IslandTestCase):
     """
     Make sure to set server environment to `testing` in server.json! Otherwise this will mess up your mongo instance and
     won't work.
@@ -15,7 +15,11 @@ class TestMonkey(TestCase):
     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_is_dead(self):
+        self.fail_if_not_testing_env()
+        self.clean_monkey_db()
+
         # Arrange
         alive_monkey_ttl = MonkeyTtl.create_ttl_expire_in(30)
         alive_monkey_ttl.save()
@@ -43,6 +47,9 @@ class TestMonkey(TestCase):
         self.assertFalse(alive_monkey.is_dead())
 
     def test_get_single_monkey_by_id(self):
+        self.fail_if_not_testing_env()
+        self.clean_monkey_db()
+
         # Arrange
         a_monkey = Monkey(guid=str(uuid.uuid4()))
         a_monkey.save()
@@ -52,3 +59,21 @@ class TestMonkey(TestCase):
         self.assertIsNotNone(Monkey.get_single_monkey_by_id(a_monkey.id))
         # Raise on non-existent monkey
         self.assertRaises(MonkeyNotFoundError, Monkey.get_single_monkey_by_id, "abcdefabcdefabcdefabcdef")
+
+    def test_get_os(self):
+        self.fail_if_not_testing_env()
+        self.clean_monkey_db()
+
+        linux_monkey = Monkey(guid=str(uuid.uuid4()),
+                              description="Linux shay-Virtual-Machine 4.15.0-50-generic #54-Ubuntu SMP Mon May 6 18:46:08 UTC 2019 x86_64 x86_64")
+        windows_monkey = Monkey(guid=str(uuid.uuid4()),
+                                description="Windows bla bla bla")
+        unknown_monkey = Monkey(guid=str(uuid.uuid4()),
+                                description="bla bla bla")
+        linux_monkey.save()
+        windows_monkey.save()
+        unknown_monkey.save()
+
+        self.assertEquals(1, len(filter(lambda m: m.get_os() == "windows", Monkey.objects())))
+        self.assertEquals(1, len(filter(lambda m: m.get_os() == "linux", Monkey.objects())))
+        self.assertEquals(1, len(filter(lambda m: m.get_os() == "unknown", Monkey.objects())))
diff --git a/monkey/monkey_island/cc/testing/IslandTestCase.py b/monkey/monkey_island/cc/testing/IslandTestCase.py
new file mode 100644
index 000000000..e894f13df
--- /dev/null
+++ b/monkey/monkey_island/cc/testing/IslandTestCase.py
@@ -0,0 +1,12 @@
+import unittest
+from monkey_island.cc.environment.environment import env
+from monkey_island.cc.models import Monkey
+
+
+class IslandTestCase(unittest.TestCase):
+    def fail_if_not_testing_env(self):
+        self.failIf(not env.testing, "Change server_config.json to testing environment.")
+
+    @staticmethod
+    def clean_monkey_db():
+        Monkey.objects().delete()
diff --git a/monkey/monkey_island/cc/testing/__init__.py b/monkey/monkey_island/cc/testing/__init__.py
new file mode 100644
index 000000000..e69de29bb

From 712ce4622d145c47fc3c38e1527f03dc1ab6fa6e Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 5 Jun 2019 18:28:54 +0300
Subject: [PATCH 4/4] Refactored the GenerateMapNodes function to use the DAL
 and now it filters windows nodes

---
 .../monkey_island/cc/services/pth_report.py   | 13 ++--
 .../cc/services/test_PTHReportService.py      | 69 +++++++++++++++++++
 2 files changed, 76 insertions(+), 6 deletions(-)
 create mode 100644 monkey/monkey_island/cc/services/test_PTHReportService.py

diff --git a/monkey/monkey_island/cc/services/pth_report.py b/monkey/monkey_island/cc/services/pth_report.py
index 93fd51989..9f3b9769f 100644
--- a/monkey/monkey_island/cc/services/pth_report.py
+++ b/monkey/monkey_island/cc/services/pth_report.py
@@ -1,6 +1,7 @@
 from itertools import product
 
 from monkey_island.cc.database import mongo
+from monkey_island.cc.models import Monkey
 from bson import ObjectId
 
 from monkey_island.cc.services.groups_and_users_consts import USERTYPE
@@ -216,15 +217,15 @@ class PTHReportService(object):
 
     @staticmethod
     def generate_map_nodes():
-        monkeys = mongo.db.monkey.find({}, {'_id': 1, 'hostname': 1, 'critical_services': 1, 'ip_addresses': 1})
+        monkeys = filter(lambda m: m.get_os() == "windows", Monkey.objects())
 
         return [
             {
-                'id': monkey['_id'],
-                'label': '{0} : {1}'.format(monkey['hostname'], monkey['ip_addresses'][0]),
-                'group': 'critical' if monkey.get('critical_services', []) else 'normal',
-                'services': monkey.get('critical_services', []),
-                'hostname': monkey['hostname']
+                'id': monkey.guid,
+                'label': '{0} : {1}'.format(monkey.hostname, monkey.ip_addresses[0]),
+                'group': 'critical' if monkey.critical_services is not None else 'normal',
+                'services': monkey.critical_services,
+                'hostname': monkey.hostname
             } for monkey in monkeys
         ]
 
diff --git a/monkey/monkey_island/cc/services/test_PTHReportService.py b/monkey/monkey_island/cc/services/test_PTHReportService.py
new file mode 100644
index 000000000..24e560ff0
--- /dev/null
+++ b/monkey/monkey_island/cc/services/test_PTHReportService.py
@@ -0,0 +1,69 @@
+import uuid
+
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.services.pth_report import PTHReportService
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+
+
+class TestPTHReportServiceGenerateMapNodes(IslandTestCase):
+    def test_generate_map_nodes(self):
+        self.fail_if_not_testing_env()
+        self.clean_monkey_db()
+
+        self.assertEqual(PTHReportService.generate_map_nodes(), [])
+
+        windows_monkey_with_services = Monkey(
+            guid=str(uuid.uuid4()),
+            hostname="A_Windows_PC_1",
+            critical_services=["aCriticalService", "Domain Controller"],
+            ip_addresses=["1.1.1.1", "2.2.2.2"],
+            description="windows 10"
+        )
+        windows_monkey_with_services.save()
+
+        windows_monkey_with_no_services = Monkey(
+            guid=str(uuid.uuid4()),
+            hostname="A_Windows_PC_2",
+            critical_services=[],
+            ip_addresses=["3.3.3.3"],
+            description="windows 10"
+        )
+        windows_monkey_with_no_services.save()
+
+        linux_monkey = Monkey(
+            guid=str(uuid.uuid4()),
+            hostname="A_Linux_PC",
+            ip_addresses=["4.4.4.4"],
+            description="linux ubuntu"
+        )
+        linux_monkey.save()
+
+        map_nodes = PTHReportService.generate_map_nodes()
+
+        self.assertEquals(2, len(map_nodes))
+
+    def test_generate_map_nodes_parsing(self):
+        self.fail_if_not_testing_env()
+        self.clean_monkey_db()
+
+        monkey_id = str(uuid.uuid4())
+        hostname = "A_Windows_PC_1"
+        windows_monkey_with_services = Monkey(
+            guid=monkey_id,
+            hostname=hostname,
+            critical_services=["aCriticalService", "Domain Controller"],
+            ip_addresses=["1.1.1.1"],
+            description="windows 10"
+        )
+        windows_monkey_with_services.save()
+
+        map_nodes = PTHReportService.generate_map_nodes()
+
+        self.assertEquals(map_nodes[0]["id"], monkey_id)
+        self.assertEquals(map_nodes[0]["label"], "A_Windows_PC_1 : 1.1.1.1")
+        self.assertEquals(map_nodes[0]["group"], "critical")
+        self.assertEquals(len(map_nodes[0]["services"]), 2)
+        self.assertEquals(map_nodes[0]["hostname"], hostname)
+
+
+