From d393a0b3c632b03420d0f96c868d8eed37cf09c1 Mon Sep 17 00:00:00 2001
From: vakarisz <vakarisz@yahoo.com>
Date: Fri, 17 Jun 2022 18:05:26 +0300
Subject: [PATCH 1/9] Agent: Change credential collectors, payloads and pbas in
 flat config

Flat config changes are made in order for config object to be serializable
---
 monkey/monkey_island/cc/services/config.py    | 18 +++++++---
 .../monkey_configs/flat_config.json           |  1 +
 .../monkey_island/cc/services/test_config.py  | 33 +++++++++----------
 3 files changed, 31 insertions(+), 21 deletions(-)

diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py
index 82346258f..3b466cd31 100644
--- a/monkey/monkey_island/cc/services/config.py
+++ b/monkey/monkey_island/cc/services/config.py
@@ -357,6 +357,7 @@ class ConfigService:
         ConfigService._format_payloads_from_flat_config(config)
         ConfigService._format_pbas_from_flat_config(config)
         ConfigService._format_propagation_from_flat_config(config)
+        ConfigService._format_credential_collectors(config)
 
         # Ok, I'll admit this is just sort of jammed in here. But this code is going away very soon.
         del config["HTTP_PORTS"]
@@ -376,9 +377,18 @@ class ConfigService:
         for field in fields_to_remove:
             config.pop(field, None)
 
+    @staticmethod
+    def _format_credential_collectors(config: Dict):
+        collectors = [
+            {"name": collector, "options": {}} for collector in config["credential_collectors"]
+        ]
+        config["credential_collectors"] = collectors
+
     @staticmethod
     def _format_payloads_from_flat_config(config: Dict):
-        config.setdefault("payloads", {})["ransomware"] = config["ransomware"]
+        config.setdefault("payloads", []).append(
+            {"name": "ransomware", "options": config["ransomware"]}
+        )
         config.pop("ransomware", None)
 
     @staticmethod
@@ -388,9 +398,9 @@ class ConfigService:
         flat_windows_command_field = "custom_PBA_windows_cmd"
         flat_windows_filename_field = "PBA_windows_filename"
 
-        formatted_pbas_config = {}
-        for pba in config.get("post_breach_actions", []):
-            formatted_pbas_config[pba] = {}
+        formatted_pbas_config = [
+            {"name": pba, "options": {}} for pba in config.get("post_breach_actions", [])
+        ]
 
         config["custom_pbas"] = {
             "linux_command": config.get(flat_linux_command_field, ""),
diff --git a/monkey/tests/data_for_tests/monkey_configs/flat_config.json b/monkey/tests/data_for_tests/monkey_configs/flat_config.json
index 33bf50da1..441c176ed 100644
--- a/monkey/tests/data_for_tests/monkey_configs/flat_config.json
+++ b/monkey/tests/data_for_tests/monkey_configs/flat_config.json
@@ -27,6 +27,7 @@
             "private_key": "my_private_key"
         }
     ],
+    "credential_collectors": ["MimikatzCollector", "SSHCollector"],
     "exploit_user_list": [
         "Administrator",
         "root",
diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
index 9e01b8365..108659ddf 100644
--- a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
+++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
@@ -25,34 +25,33 @@ def test_format_config_for_agent__credentials_removed():
 
 def test_format_config_for_agent__ransomware_payload():
     expected_ransomware_options = {
-        "ransomware": {
-            "encryption": {
-                "enabled": True,
-                "directories": {
-                    "linux_target_dir": "/tmp/ransomware-target",
-                    "windows_target_dir": "C:\\windows\\temp\\ransomware-target",
-                },
+        "encryption": {
+            "enabled": True,
+            "directories": {
+                "linux_target_dir": "/tmp/ransomware-target",
+                "windows_target_dir": "C:\\windows\\temp\\ransomware-target",
             },
-            "other_behaviors": {"readme": True},
-        }
+        },
+        "other_behaviors": {"readme": True},
     }
 
     flat_monkey_config = ConfigService.format_flat_config_for_agent()
 
     assert "payloads" in flat_monkey_config
-    assert flat_monkey_config["payloads"] == expected_ransomware_options
+    assert flat_monkey_config["payloads"][0]["name"] == "ransomware"
+    assert flat_monkey_config["payloads"][0]["options"] == expected_ransomware_options
 
     assert "ransomware" not in flat_monkey_config
 
 
 def test_format_config_for_agent__pbas():
-    expected_pbas_config = {
-        "CommunicateAsBackdoorUser": {},
-        "ModifyShellStartupFiles": {},
-        "ScheduleJobs": {},
-        "Timestomping": {},
-        "AccountDiscovery": {},
-    }
+    expected_pbas_config = [
+        {"name": "CommunicateAsBackdoorUser", "options": {}},
+        {"name": "ModifyShellStartupFiles", "options": {}},
+        {"name": "ScheduleJobs", "options": {}},
+        {"name": "Timestomping", "options": {}},
+        {"name": "AccountDiscovery", "options": {}},
+    ]
     flat_monkey_config = ConfigService.format_flat_config_for_agent()
 
     assert "post_breach_actions" in flat_monkey_config

From 83dd4334b22fe6398ce1f573243bcb4dc578dffa Mon Sep 17 00:00:00 2001
From: Shreya Malviya <shreya.malviya@gmail.com>
Date: Sun, 19 Jun 2022 21:54:54 -0700
Subject: [PATCH 2/9] UT: Fix flat_config.json to match current config schema

---
 monkey/tests/data_for_tests/monkey_configs/flat_config.json | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/monkey/tests/data_for_tests/monkey_configs/flat_config.json b/monkey/tests/data_for_tests/monkey_configs/flat_config.json
index 441c176ed..42568404a 100644
--- a/monkey/tests/data_for_tests/monkey_configs/flat_config.json
+++ b/monkey/tests/data_for_tests/monkey_configs/flat_config.json
@@ -9,7 +9,6 @@
     ],
     "PBA_linux_filename": "test.sh",
     "PBA_windows_filename": "test.ps1",
-    "alive": true,
     "blocked_ips": ["192.168.1.1", "192.168.1.100"],
     "custom_PBA_linux_cmd": "bash test.sh",
     "custom_PBA_windows_cmd": "powershell test.ps1",
@@ -54,7 +53,6 @@
     "inaccessible_subnets": ["10.0.0.0/24", "10.0.10.0/24"],
     "keep_tunnel_open_time": 60,
     "local_network_scan": true,
-    "max_depth": null,
     "ping_scan_timeout": 1000,
     "post_breach_actions": [
         "CommunicateAsBackdoorUser",
@@ -76,9 +74,6 @@
         }
     },
     "subnet_scan_list": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
-    "system_info_collector_classes": [
-        "MimikatzCollector"
-    ],
     "tcp_scan_timeout": 3000,
     "tcp_target_ports": [
         22,

From f9a7989f5e150cb51852b402a45210d2523432ba Mon Sep 17 00:00:00 2001
From: Shreya Malviya <shreya.malviya@gmail.com>
Date: Sun, 19 Jun 2022 22:07:47 -0700
Subject: [PATCH 3/9] Island: Fix 'propagation' field of config flattening

---
 monkey/monkey_island/cc/services/config.py | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py
index 3b466cd31..aca2217e3 100644
--- a/monkey/monkey_island/cc/services/config.py
+++ b/monkey/monkey_island/cc/services/config.py
@@ -418,24 +418,24 @@ class ConfigService:
 
     @staticmethod
     def _format_propagation_from_flat_config(config: Dict):
-        formatted_propagation_config = {"network_scan": {}, "targets": {}}
+        formatted_propagation_config = {"network_scan": {}, "maximum_depth": {}, "exploiters": {}}
 
         formatted_propagation_config[
             "network_scan"
         ] = ConfigService._format_network_scan_from_flat_config(config)
 
-        formatted_propagation_config["targets"] = ConfigService._format_targets_from_flat_config(
-            config
-        )
         formatted_propagation_config[
             "exploiters"
         ] = ConfigService._format_exploiters_from_flat_config(config)
 
+        formatted_propagation_config["maximum_depth"] = config["depth"]
+        del config["depth"]
+
         config["propagation"] = formatted_propagation_config
 
     @staticmethod
     def _format_network_scan_from_flat_config(config: Dict) -> Dict[str, Any]:
-        formatted_network_scan_config = {"tcp": {}, "icmp": {}, "fingerprinters": []}
+        formatted_network_scan_config = {"tcp": {}, "icmp": {}, "fingerprinters": [], "targets": {}}
 
         formatted_network_scan_config["tcp"] = ConfigService._format_tcp_scan_from_flat_config(
             config
@@ -447,6 +447,10 @@ class ConfigService:
             "fingerprinters"
         ] = ConfigService._format_fingerprinters_from_flat_config(config)
 
+        formatted_network_scan_config["targets"] = ConfigService._format_targets_from_flat_config(
+            config
+        )
+
         return formatted_network_scan_config
 
     @staticmethod
@@ -457,7 +461,7 @@ class ConfigService:
 
         formatted_tcp_scan_config = {}
 
-        formatted_tcp_scan_config["timeout_ms"] = config[flat_tcp_timeout_field]
+        formatted_tcp_scan_config["timeout"] = config[flat_tcp_timeout_field]
 
         ports = ConfigService._union_tcp_and_http_ports(
             config[flat_tcp_ports_field], config[flat_http_ports_field]
@@ -481,7 +485,7 @@ class ConfigService:
         flat_ping_timeout_field = "ping_scan_timeout"
 
         formatted_icmp_scan_config = {}
-        formatted_icmp_scan_config["timeout_ms"] = config[flat_ping_timeout_field]
+        formatted_icmp_scan_config["timeout"] = config[flat_ping_timeout_field]
 
         config.pop(flat_ping_timeout_field, None)
 
@@ -529,7 +533,7 @@ class ConfigService:
         formatted_scan_targets_config[flat_local_network_scan_field] = config[
             flat_local_network_scan_field
         ]
-        formatted_scan_targets_config[flat_subnet_scan_list_field] = config[
+        formatted_scan_targets_config["subnets"] = config[
             flat_subnet_scan_list_field
         ]
 

From ba3af5a9c293a5e4b375426af7ee42a2389305aa Mon Sep 17 00:00:00 2001
From: Shreya Malviya <shreya.malviya@gmail.com>
Date: Sun, 19 Jun 2022 22:13:54 -0700
Subject: [PATCH 4/9] Island: Fix 'exploitation' field of config flattening

---
 monkey/monkey_island/cc/services/config.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py
index aca2217e3..b47c0edd5 100644
--- a/monkey/monkey_island/cc/services/config.py
+++ b/monkey/monkey_island/cc/services/config.py
@@ -418,14 +418,14 @@ class ConfigService:
 
     @staticmethod
     def _format_propagation_from_flat_config(config: Dict):
-        formatted_propagation_config = {"network_scan": {}, "maximum_depth": {}, "exploiters": {}}
+        formatted_propagation_config = {"network_scan": {}, "maximum_depth": {}, "exploitation": {}}
 
         formatted_propagation_config[
             "network_scan"
         ] = ConfigService._format_network_scan_from_flat_config(config)
 
         formatted_propagation_config[
-            "exploiters"
+            "exploitation"
         ] = ConfigService._format_exploiters_from_flat_config(config)
 
         formatted_propagation_config["maximum_depth"] = config["depth"]

From ab23b3c9cb3024994ee0bb02144fc9e71bbab0f4 Mon Sep 17 00:00:00 2001
From: Shreya Malviya <shreya.malviya@gmail.com>
Date: Sun, 19 Jun 2022 23:11:00 -0700
Subject: [PATCH 5/9] Island: Fix exploiters' `supported_os`'s capitalisation
 in config flattening

---
 monkey/monkey_island/cc/services/config.py | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py
index b47c0edd5..684801ac2 100644
--- a/monkey/monkey_island/cc/services/config.py
+++ b/monkey/monkey_island/cc/services/config.py
@@ -600,14 +600,14 @@ class ConfigService:
         formatted_config: Dict,
     ) -> Dict[str, List[Dict[str, Any]]]:
         supported_os = {
-            "HadoopExploiter": ["linux", "windows"],
-            "Log4ShellExploiter": ["linux", "windows"],
-            "MSSQLExploiter": ["windows"],
-            "PowerShellExploiter": ["windows"],
-            "SSHExploiter": ["linux"],
-            "SmbExploiter": ["windows"],
-            "WmiExploiter": ["windows"],
-            "ZerologonExploiter": ["windows"],
+            "HadoopExploiter": ["LINUX", "WINDOWS"],
+            "Log4ShellExploiter": ["LINUX", "WINDOWS"],
+            "MSSQLExploiter": ["WINDOWS"],
+            "PowerShellExploiter": ["WINDOWS"],
+            "SSHExploiter": ["LINUX"],
+            "SmbExploiter": ["WINDOWS"],
+            "WmiExploiter": ["WINDOWS"],
+            "ZerologonExploiter": ["WINDOWS"],
         }
         new_config = copy.deepcopy(formatted_config)
         for exploiter in chain(new_config["brute_force"], new_config["vulnerability"]):

From 02dcee8bfceee04f3d37267c79909d4a9465b96c Mon Sep 17 00:00:00 2001
From: Shreya Malviya <shreya.malviya@gmail.com>
Date: Sun, 19 Jun 2022 23:35:35 -0700
Subject: [PATCH 6/9] UT: Modify tests to pass with config flattening changes

---
 .../monkey_island/cc/services/test_config.py  | 67 ++++++++++---------
 1 file changed, 36 insertions(+), 31 deletions(-)

diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
index 108659ddf..b70a6bd3b 100644
--- a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
+++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
@@ -92,32 +92,14 @@ def test_format_config_for_agent__propagation():
     flat_monkey_config = ConfigService.format_flat_config_for_agent()
 
     assert "propagation" in flat_monkey_config
-    assert "targets" in flat_monkey_config["propagation"]
     assert "network_scan" in flat_monkey_config["propagation"]
-    assert "exploiters" in flat_monkey_config["propagation"]
-
-
-def test_format_config_for_agent__propagation_targets():
-    expected_targets = {
-        "blocked_ips": ["192.168.1.1", "192.168.1.100"],
-        "inaccessible_subnets": ["10.0.0.0/24", "10.0.10.0/24"],
-        "local_network_scan": True,
-        "subnet_scan_list": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
-    }
-
-    flat_monkey_config = ConfigService.format_flat_config_for_agent()
-
-    assert flat_monkey_config["propagation"]["targets"] == expected_targets
-    assert "blocked_ips" not in flat_monkey_config
-    assert "inaccessible_subnets" not in flat_monkey_config
-    assert "local_network_scan" not in flat_monkey_config
-    assert "subnet_scan_list" not in flat_monkey_config
+    assert "exploitation" in flat_monkey_config["propagation"]
 
 
 def test_format_config_for_agent__network_scan():
     expected_network_scan_config = {
         "tcp": {
-            "timeout_ms": 3000,
+            "timeout": 3000,
             "ports": [
                 22,
                 80,
@@ -135,7 +117,13 @@ def test_format_config_for_agent__network_scan():
             ],
         },
         "icmp": {
-            "timeout_ms": 1000,
+            "timeout": 1000,
+        },
+        "targets": {
+            "blocked_ips": ["192.168.1.1", "192.168.1.100"],
+            "inaccessible_subnets": ["10.0.0.0/24", "10.0.10.0/24"],
+            "local_network_scan": True,
+            "subnets": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
         },
         "fingerprinters": [
             {"name": "elastic", "options": {}},
@@ -160,36 +148,53 @@ def test_format_config_for_agent__network_scan():
     assert "finger_classes" not in flat_monkey_config
 
 
+def test_format_config_for_agent__propagation_network_scan_targets():
+    expected_targets = {
+        "blocked_ips": ["192.168.1.1", "192.168.1.100"],
+        "inaccessible_subnets": ["10.0.0.0/24", "10.0.10.0/24"],
+        "local_network_scan": True,
+        "subnets": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
+    }
+
+    flat_monkey_config = ConfigService.format_flat_config_for_agent()
+
+    assert flat_monkey_config["propagation"]["network_scan"]["targets"] == expected_targets
+    assert "blocked_ips" not in flat_monkey_config
+    assert "inaccessible_subnets" not in flat_monkey_config
+    assert "local_network_scan" not in flat_monkey_config
+    assert "subnet_scan_list" not in flat_monkey_config
+
+
 def test_format_config_for_agent__exploiters():
     expected_exploiters_config = {
         "options": {
             "http_ports": [80, 443, 7001, 8008, 8080, 9200],
         },
         "brute_force": [
-            {"name": "MSSQLExploiter", "supported_os": ["windows"], "options": {}},
-            {"name": "PowerShellExploiter", "supported_os": ["windows"], "options": {}},
-            {"name": "SSHExploiter", "supported_os": ["linux"], "options": {}},
+            {"name": "MSSQLExploiter", "supported_os": ["WINDOWS"], "options": {}},
+            {"name": "PowerShellExploiter", "supported_os": ["WINDOWS"], "options": {}},
+            {"name": "SSHExploiter", "supported_os": ["LINUX"], "options": {}},
             {
                 "name": "SmbExploiter",
-                "supported_os": ["windows"],
+                "supported_os": ["WINDOWS"],
                 "options": {"smb_download_timeout": 30},
             },
             {
                 "name": "WmiExploiter",
-                "supported_os": ["windows"],
+                "supported_os": ["WINDOWS"],
                 "options": {"smb_download_timeout": 30},
             },
         ],
         "vulnerability": [
-            {"name": "HadoopExploiter", "supported_os": ["linux", "windows"], "options": {}},
-            {"name": "Log4ShellExploiter", "supported_os": ["linux", "windows"], "options": {}},
-            {"name": "ZerologonExploiter", "supported_os": ["windows"], "options": {}},
+            {"name": "HadoopExploiter", "supported_os": ["LINUX", "WINDOWS"], "options": {}},
+            {"name": "Log4ShellExploiter", "supported_os": ["LINUX", "WINDOWS"], "options": {}},
+            {"name": "ZerologonExploiter", "supported_os": ["WINDOWS"], "options": {}},
         ],
     }
     flat_monkey_config = ConfigService.format_flat_config_for_agent()
 
     assert "propagation" in flat_monkey_config
-    assert "exploiters" in flat_monkey_config["propagation"]
+    assert "exploitation" in flat_monkey_config["propagation"]
 
-    assert flat_monkey_config["propagation"]["exploiters"] == expected_exploiters_config
+    assert flat_monkey_config["propagation"]["exploitation"] == expected_exploiters_config
     assert "exploiter_classes" not in flat_monkey_config

From c0f0d35f0b531be606b5c230504466cdabd3a07b Mon Sep 17 00:00:00 2001
From: vakarisz <vakarisz@yahoo.com>
Date: Mon, 20 Jun 2022 12:18:03 +0300
Subject: [PATCH 7/9] Island: Use OperatingSystems enum in config.py

---
 monkey/monkey_island/cc/services/config.py | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py
index 684801ac2..86541c33d 100644
--- a/monkey/monkey_island/cc/services/config.py
+++ b/monkey/monkey_island/cc/services/config.py
@@ -8,6 +8,7 @@ from typing import Any, Dict, List
 
 from jsonschema import Draft4Validator, validators
 
+from common import OperatingSystems
 from common.config_value_paths import (
     LM_HASH_LIST_PATH,
     NTLM_HASH_LIST_PATH,
@@ -533,9 +534,7 @@ class ConfigService:
         formatted_scan_targets_config[flat_local_network_scan_field] = config[
             flat_local_network_scan_field
         ]
-        formatted_scan_targets_config["subnets"] = config[
-            flat_subnet_scan_list_field
-        ]
+        formatted_scan_targets_config["subnets"] = config[flat_subnet_scan_list_field]
 
         config.pop(flat_blocked_ips_field, None)
         config.pop(flat_inaccessible_subnets_field, None)
@@ -600,14 +599,14 @@ class ConfigService:
         formatted_config: Dict,
     ) -> Dict[str, List[Dict[str, Any]]]:
         supported_os = {
-            "HadoopExploiter": ["LINUX", "WINDOWS"],
-            "Log4ShellExploiter": ["LINUX", "WINDOWS"],
-            "MSSQLExploiter": ["WINDOWS"],
-            "PowerShellExploiter": ["WINDOWS"],
-            "SSHExploiter": ["LINUX"],
-            "SmbExploiter": ["WINDOWS"],
-            "WmiExploiter": ["WINDOWS"],
-            "ZerologonExploiter": ["WINDOWS"],
+            "HadoopExploiter": [OperatingSystems.LINUX, OperatingSystems.WINDOWS],
+            "Log4ShellExploiter": [OperatingSystems.LINUX, OperatingSystems.WINDOWS],
+            "MSSQLExploiter": [OperatingSystems.WINDOWS],
+            "PowerShellExploiter": [OperatingSystems.WINDOWS],
+            "SSHExploiter": [OperatingSystems.LINUX],
+            "SmbExploiter": [OperatingSystems.WINDOWS],
+            "WmiExploiter": [OperatingSystems.WINDOWS],
+            "ZerologonExploiter": [OperatingSystems.WINDOWS],
         }
         new_config = copy.deepcopy(formatted_config)
         for exploiter in chain(new_config["brute_force"], new_config["vulnerability"]):

From 0474e2a5f79f90b95c8de765ba2b2aeb8548afdc Mon Sep 17 00:00:00 2001
From: vakarisz <vakarisz@yahoo.com>
Date: Mon, 20 Jun 2022 12:27:01 +0300
Subject: [PATCH 8/9] Island: Change json encoding to encode Enums to name
 string

Enum objects couldn't get encoded, so for each enum we had to decide whether the name or the value would be used to represent that enum value. Changing the encoding to name allows us to use enum object on the island without having to worry about encoding.
---
 .../cc/services/representations.py            | 25 +++++++++++++------
 .../cc/services/test_representations.py       |  9 +++++++
 2 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/monkey/monkey_island/cc/services/representations.py b/monkey/monkey_island/cc/services/representations.py
index 0193fae0d..8a1e849a6 100644
--- a/monkey/monkey_island/cc/services/representations.py
+++ b/monkey/monkey_island/cc/services/representations.py
@@ -1,4 +1,5 @@
 from datetime import datetime
+from enum import Enum
 
 import bson
 from bson.json_util import dumps
@@ -11,19 +12,27 @@ def normalize_obj(obj):
         del obj["_id"]
 
     for key, value in list(obj.items()):
-        if isinstance(value, bson.objectid.ObjectId):
-            obj[key] = str(value)
-        if isinstance(value, datetime):
-            obj[key] = str(value)
-        if isinstance(value, dict):
-            obj[key] = normalize_obj(value)
         if isinstance(value, list):
             for i in range(0, len(value)):
-                if isinstance(value[i], dict):
-                    value[i] = normalize_obj(value[i])
+                obj[key][i] = _normalize_value(value[i])
+        else:
+            obj[key] = _normalize_value(value)
     return obj
 
 
+def _normalize_value(value):
+    if type(value) == dict:
+        return normalize_obj(value)
+    if isinstance(value, bson.objectid.ObjectId):
+        return str(value)
+    if isinstance(value, datetime):
+        return str(value)
+    if issubclass(type(value), Enum):
+        return value.name
+    else:
+        return value
+
+
 def output_json(obj, code, headers=None):
     obj = normalize_obj(obj)
     resp = make_response(dumps(obj), code)
diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_representations.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_representations.py
index c088c3dce..e40e4470f 100644
--- a/monkey/tests/unit_tests/monkey_island/cc/services/test_representations.py
+++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_representations.py
@@ -1,4 +1,5 @@
 from datetime import datetime
+from enum import Enum
 from unittest import TestCase
 
 import bson
@@ -44,3 +45,11 @@ class TestRepresentations(TestCase):
                 }
             ),
         )
+
+    def test_normalize__enum(self):
+        class BogusEnum(Enum):
+            bogus_val = "Bogus"
+
+        my_obj = {"something": "something", "my_enum": BogusEnum.bogus_val}
+
+        assert {"something": "something", "my_enum": "bogus_val"} == normalize_obj(my_obj)

From 3757e3318026318e994e6bace5b65c06a39d0fc3 Mon Sep 17 00:00:00 2001
From: vakarisz <vakarisz@yahoo.com>
Date: Mon, 20 Jun 2022 12:30:25 +0300
Subject: [PATCH 9/9] UT: Use OperatingSystems enum instead of strings

---
 .../monkey_island/cc/services/test_config.py  | 33 ++++++++++++++-----
 1 file changed, 25 insertions(+), 8 deletions(-)

diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
index b70a6bd3b..df866e388 100644
--- a/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
+++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_config.py
@@ -1,5 +1,6 @@
 import pytest
 
+from common import OperatingSystems
 from monkey_island.cc.services.config import ConfigService
 
 # If tests fail because config path is changed, sync with
@@ -171,24 +172,40 @@ def test_format_config_for_agent__exploiters():
             "http_ports": [80, 443, 7001, 8008, 8080, 9200],
         },
         "brute_force": [
-            {"name": "MSSQLExploiter", "supported_os": ["WINDOWS"], "options": {}},
-            {"name": "PowerShellExploiter", "supported_os": ["WINDOWS"], "options": {}},
-            {"name": "SSHExploiter", "supported_os": ["LINUX"], "options": {}},
+            {"name": "MSSQLExploiter", "supported_os": [OperatingSystems.WINDOWS], "options": {}},
+            {
+                "name": "PowerShellExploiter",
+                "supported_os": [OperatingSystems.WINDOWS],
+                "options": {},
+            },
+            {"name": "SSHExploiter", "supported_os": [OperatingSystems.LINUX], "options": {}},
             {
                 "name": "SmbExploiter",
-                "supported_os": ["WINDOWS"],
+                "supported_os": [OperatingSystems.WINDOWS],
                 "options": {"smb_download_timeout": 30},
             },
             {
                 "name": "WmiExploiter",
-                "supported_os": ["WINDOWS"],
+                "supported_os": [OperatingSystems.WINDOWS],
                 "options": {"smb_download_timeout": 30},
             },
         ],
         "vulnerability": [
-            {"name": "HadoopExploiter", "supported_os": ["LINUX", "WINDOWS"], "options": {}},
-            {"name": "Log4ShellExploiter", "supported_os": ["LINUX", "WINDOWS"], "options": {}},
-            {"name": "ZerologonExploiter", "supported_os": ["WINDOWS"], "options": {}},
+            {
+                "name": "HadoopExploiter",
+                "supported_os": [OperatingSystems.LINUX, OperatingSystems.WINDOWS],
+                "options": {},
+            },
+            {
+                "name": "Log4ShellExploiter",
+                "supported_os": [OperatingSystems.LINUX, OperatingSystems.WINDOWS],
+                "options": {},
+            },
+            {
+                "name": "ZerologonExploiter",
+                "supported_os": [OperatingSystems.WINDOWS],
+                "options": {},
+            },
         ],
     }
     flat_monkey_config = ConfigService.format_flat_config_for_agent()