From c5e142a5005a7dc7df51bb9a0946f68cf6bbdaa4 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Wed, 13 Mar 2024 17:43:56 +0000 Subject: [PATCH 01/10] 2299: Remove calls to corrupt. --- src/primaite/simulator/file_system/file.py | 3 --- .../simulator/file_system/file_system_item_abc.py | 2 +- src/primaite/simulator/file_system/folder.py | 10 +++++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/primaite/simulator/file_system/file.py b/src/primaite/simulator/file_system/file.py index 9331c40c..10819522 100644 --- a/src/primaite/simulator/file_system/file.py +++ b/src/primaite/simulator/file_system/file.py @@ -162,9 +162,6 @@ class File(FileSystemItemABC): if self.previous_hash is None: self.previous_hash = current_hash - # if the previous hash and current hash do not match, mark file as corrupted - if self.previous_hash is not current_hash: - self.corrupt() return True def repair(self) -> bool: diff --git a/src/primaite/simulator/file_system/file_system_item_abc.py b/src/primaite/simulator/file_system/file_system_item_abc.py index 32f5f6be..c89152b4 100644 --- a/src/primaite/simulator/file_system/file_system_item_abc.py +++ b/src/primaite/simulator/file_system/file_system_item_abc.py @@ -156,7 +156,7 @@ class FileSystemItemABC(SimComponent): @abstractmethod def check_hash(self) -> bool: """ - Checks the has of the file to detect any changes. + Checks the hash of the file to detect any changes. For current implementation, any change in file hash means it is compromised. diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index 6ebd8d14..192d8627 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -381,17 +381,17 @@ class Folder(FileSystemItemABC): return False # iterate through the files and run a check hash - no_corrupted_files = True + # no_corrupted_files = True for file_id in self.files: file = self.get_file_by_id(file_uuid=file_id) file.check_hash() - if file.health_status == FileSystemItemHealthStatus.CORRUPT: - no_corrupted_files = False + # if file.health_status == FileSystemItemHealthStatus.CORRUPT: + # no_corrupted_files = False # if one file in the folder is corrupted, set the folder status to corrupted - if not no_corrupted_files: - self.corrupt() + # if not no_corrupted_files: + # self.corrupt() self.sys_log.info(f"Checking hash of folder {self.name} (id: {self.uuid})") return True From 90224960e53dedde0d8da55a1b6f4d2bbd577017 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Fri, 15 Mar 2024 10:33:58 +0000 Subject: [PATCH 02/10] 2299: Backup Jupyter notebook changes. --- .../Data-Manipulation-E2E-Demonstration.ipynb | 271 ++++++++++++++---- 1 file changed, 221 insertions(+), 50 deletions(-) diff --git a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb index 1d7cb157..d4617d61 100644 --- a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb @@ -352,7 +352,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "tags": [] }, @@ -364,7 +364,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "tags": [] }, @@ -389,9 +389,162 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-03-13 16:52:48,201: Resetting environment, episode 0, avg. reward: 0.0\n", + "2024-03-13 16:52:48,205: Saving agent action log to C:\\Users\\NickTodd\\primaite\\3.0.0b6\\sessions\\2024-03-13\\16-52-48\\agent_actions\\episode_0.json\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "env created successfully\n", + "{'ACL': {1: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 0,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 2: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 1,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 3: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 2,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 4: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 3,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 5: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 4,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 6: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 5,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 7: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 6,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 8: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 7,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 9: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 8,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0},\n", + " 10: {'dest_node_id': 0,\n", + " 'dest_port': 0,\n", + " 'permission': 0,\n", + " 'position': 9,\n", + " 'protocol': 0,\n", + " 'source_node_id': 0,\n", + " 'source_port': 0}},\n", + " 'ICS': 0,\n", + " 'LINKS': {1: {'PROTOCOLS': {'ALL': 1}},\n", + " 2: {'PROTOCOLS': {'ALL': 1}},\n", + " 3: {'PROTOCOLS': {'ALL': 1}},\n", + " 4: {'PROTOCOLS': {'ALL': 1}},\n", + " 5: {'PROTOCOLS': {'ALL': 1}},\n", + " 6: {'PROTOCOLS': {'ALL': 1}},\n", + " 7: {'PROTOCOLS': {'ALL': 1}},\n", + " 8: {'PROTOCOLS': {'ALL': 1}},\n", + " 9: {'PROTOCOLS': {'ALL': 1}},\n", + " 10: {'PROTOCOLS': {'ALL': 0}}},\n", + " 'NODES': {1: {'FOLDERS': {1: {'FILES': {1: {'health_status': 0}},\n", + " 'health_status': 0}},\n", + " 'NICS': {1: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 1},\n", + " 2: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 0}},\n", + " 'SERVICES': {1: {'health_status': 0, 'operating_status': 1}},\n", + " 'operating_status': 1},\n", + " 2: {'FOLDERS': {1: {'FILES': {1: {'health_status': 0}},\n", + " 'health_status': 0}},\n", + " 'NICS': {1: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 1},\n", + " 2: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 0}},\n", + " 'SERVICES': {1: {'health_status': 0, 'operating_status': 1}},\n", + " 'operating_status': 1},\n", + " 3: {'FOLDERS': {1: {'FILES': {1: {'health_status': 1}},\n", + " 'health_status': 1}},\n", + " 'NICS': {1: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 1},\n", + " 2: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 0}},\n", + " 'SERVICES': {1: {'health_status': 0, 'operating_status': 0}},\n", + " 'operating_status': 1},\n", + " 4: {'FOLDERS': {1: {'FILES': {1: {'health_status': 0}},\n", + " 'health_status': 0}},\n", + " 'NICS': {1: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 1},\n", + " 2: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 0}},\n", + " 'SERVICES': {1: {'health_status': 0, 'operating_status': 0}},\n", + " 'operating_status': 1},\n", + " 5: {'FOLDERS': {1: {'FILES': {1: {'health_status': 0}},\n", + " 'health_status': 0}},\n", + " 'NICS': {1: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 1},\n", + " 2: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 0}},\n", + " 'SERVICES': {1: {'health_status': 0, 'operating_status': 0}},\n", + " 'operating_status': 1},\n", + " 6: {'FOLDERS': {1: {'FILES': {1: {'health_status': 0}},\n", + " 'health_status': 0}},\n", + " 'NICS': {1: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 1},\n", + " 2: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 0}},\n", + " 'SERVICES': {1: {'health_status': 0, 'operating_status': 0}},\n", + " 'operating_status': 1},\n", + " 7: {'FOLDERS': {1: {'FILES': {1: {'health_status': 0}},\n", + " 'health_status': 0}},\n", + " 'NICS': {1: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 1},\n", + " 2: {'NMNE': {'inbound': 0, 'outbound': 0},\n", + " 'nic_status': 0}},\n", + " 'SERVICES': {1: {'health_status': 0, 'operating_status': 0}},\n", + " 'operating_status': 1}}}\n" + ] + } + ], "source": [ "# create the env\n", "with open(data_manipulation_config_path(), 'r') as f:\n", @@ -410,6 +563,46 @@ "pprint(obs)" ] }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "res = FileSystemItemHealthStatus.GOOD\n", + "res = FileSystemItemHealthStatus.GOOD\n", + "res = FileSystemItemHealthStatus.COMPROMISED\n", + "res = FileSystemItemHealthStatus.COMPROMISED\n" + ] + } + ], + "source": [ + "# Test NODE_FOLDER_CHECKHASH\n", + "res = env.game.simulation.network.get_node_by_hostname('database_server').file_system.get_folder(folder_name = 'database').health_status\n", + "print(f'Folder status = {res}')\n", + "obs, reward, terminated, truncated, info = env.step(15)\n", + "obs, reward, terminated, truncated, info = env.step(14) # scan database folder\n", + "\n", + "res = env.game.simulation.network.get_node_by_hostname('database_server').file_system.get_folder(folder_name = 'database').health_status\n", + "print(f'Folder status = {res}')\n", + "\n", + "\n", + "\n", + "# Test NODE_FILE_CHECKHASH\n", + "res = env.game.simulation.network.get_node_by_hostname('database_server').file_system.get_file(folder_name = 'database', file_name = 'database.db').health_status\n", + "print(f'File status = {res}')\n", + "obs, reward, terminated, truncated, info = env.step(10)\n", + "obs, reward, terminated, truncated, info = env.step(9) # scan database file\n", + "\n", + "res = env.game.simulation.network.get_node_by_hostname('database_server').file_system.get_file(folder_name = 'database', file_name = 'database.db').health_status\n", + "print(f'File status = {res}')\n", + "\n", + "# pprint(obs['NODES'])\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -426,13 +619,13 @@ "def friendly_output_red_action(info):\n", " # parse the info dict form step output and write out what the red agent is doing\n", " red_info = info['agent_actions']['data_manipulation_attacker']\n", - " red_action = red_info['action']\n", + " red_action = red_info[0]\n", " if red_action == 'DONOTHING':\n", " red_str = 'DO NOTHING'\n", " elif red_action == 'NODE_APPLICATION_EXECUTE':\n", - " client = \"client 1\" if red_info['parameters']['node_id'] == 0 else \"client 2\"\n", + " client = \"client 1\" if red_info[1]['node_id'] == 0 else \"client 2\"\n", " red_str = f\"ATTACK from {client}\"\n", - " return red_str\n" + " return red_str" ] }, { @@ -477,7 +670,8 @@ "source": [ "obs, reward, terminated, truncated, info = env.step(9) # scan database file\n", "obs, reward, terminated, truncated, info = env.step(1) # scan webapp service\n", - "pprint(obs['NODES'])" + "\n", + "pprint(obs['NODES'])\n" ] }, { @@ -492,7 +686,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Also, the NMNE outbound of either client 1 (node 6) or client 2 (node 7) increased from 0 to 1, but only right after the red attack, so we probably cannot see it now." + "Also, the NMNE outbound of either client 1 (node 6) or client 2 (node 7) has increased from 0 to 1. This tells us which client is being used by the red agent." ] }, { @@ -510,9 +704,9 @@ "source": [ "obs, reward, terminated, truncated, info = env.step(13) # patch the database\n", "print(f\"step: {env.game.step_counter}\")\n", - "print(f\"Red action: {info['agent_actions']['data_manipulation_attacker']['action']}\" )\n", - "print(f\"Green action: {info['agent_actions']['client_1_green_user']['action']}\" )\n", - "print(f\"Green action: {info['agent_actions']['client_2_green_user']['action']}\" )\n", + "print(f\"Red action: {info['agent_actions']['data_manipulation_attacker'][0]}\" )\n", + "print(f\"Green action: {info['agent_actions']['client_1_green_user'][0]}\" )\n", + "print(f\"Green action: {info['agent_actions']['client_2_green_user'][0]}\" )\n", "print(f\"Blue reward:{reward}\" )" ] }, @@ -535,7 +729,7 @@ "source": [ "obs, reward, terminated, truncated, info = env.step(0) # patch the database\n", "print(f\"step: {env.game.step_counter}\")\n", - "print(f\"Red action: {info['agent_actions']['data_manipulation_attacker']['action']}\" )\n", + "print(f\"Red action: {info['agent_actions']['data_manipulation_attacker'][0]}\" )\n", "print(f\"Green action: {info['agent_actions']['client_2_green_user']}\" )\n", "print(f\"Green action: {info['agent_actions']['client_1_green_user']}\" )\n", "print(f\"Blue reward:{reward:.2f}\" )" @@ -557,17 +751,17 @@ "outputs": [], "source": [ "env.step(13) # Patch the database\n", - "print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker']['action']}, Blue reward:{reward:.2f}\" )\n", + "print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker'][0]}, Blue reward:{reward:.2f}\" )\n", "\n", "env.step(50) # Block client 1\n", - "print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker']['action']}, Blue reward:{reward:.2f}\" )\n", + "print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker'][0]}, Blue reward:{reward:.2f}\" )\n", "\n", "env.step(51) # Block client 2\n", - "print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker']['action']}, Blue reward:{reward:.2f}\" )\n", + "print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker'][0]}, Blue reward:{reward:.2f}\" )\n", "\n", "for step in range(30):\n", " obs, reward, terminated, truncated, info = env.step(0) # do nothing\n", - " print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker']['action']}, Blue reward:{reward:.2f}\" )" + " print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker'][0]}, Blue reward:{reward:.2f}\" )" ] }, { @@ -606,35 +800,20 @@ "metadata": {}, "outputs": [], "source": [ - "env.step(58) # Remove the ACL rule that blocks client 1\n", - "env.step(57) # Remove the ACL rule that blocks client 2\n", - "\n", - "tries = 0\n", - "while True:\n", - " tries += 1\n", - " obs, reward, terminated, truncated, info = env.step(0)\n", - "\n", - " if obs['NODES'][6]['NETWORK_INTERFACES'][1]['nmne']['outbound'] == 1:\n", - " # client 1 has NMNEs, let's block it\n", - " obs, reward, terminated, truncated, info = env.step(50) # block client 1\n", - " break\n", - " elif obs['NODES'][7]['NETWORK_INTERFACES'][1]['nmne']['outbound'] == 1:\n", - " # client 2 has NMNEs, so let's block it\n", - " obs, reward, terminated, truncated, info = env.step(51) # block client 2\n", - " break\n", - " if tries>100:\n", - " print(\"Error: NMNE never increased\")\n", - " break\n", - "\n", - "env.step(13) # Patch the database\n", - "..." + "if obs['NODES'][6]['NETWORK_INTERFACES'][1]['nmne']['outbound'] == 1:\n", + " # client 1 has NMNEs, let's unblock client 2\n", + " env.step(58) # remove ACL rule 6\n", + "elif obs['NODES'][7]['NETWORK_INTERFACES'][1]['nmne']['outbound'] == 1:\n", + " env.step(57) # remove ACL rule 5\n", + "else:\n", + " print(\"something went wrong, neither client has NMNEs\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now, the reward will eventually increase to 0.9, even after red agent attempts subsequent attacks." + "Now, the reward will eventually increase to 1.0, even after red agent attempts subsequent attacks." ] }, { @@ -643,10 +822,9 @@ "metadata": {}, "outputs": [], "source": [ - "\n", - "for step in range(40):\n", + "for step in range(30):\n", " obs, reward, terminated, truncated, info = env.step(0) # do nothing\n", - " print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker']['action']}, Blue reward:{reward:.2f}\" )" + " print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker'][0]}, Blue reward:{reward:.2f}\" )" ] }, { @@ -664,13 +842,6 @@ "source": [ "env.reset()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 228a8099a38b33b7e91ff5bfe8238146c9e821b0 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Wed, 24 Apr 2024 17:27:27 +0100 Subject: [PATCH 03/10] #2299: Revert changes to disable check_hash() --- src/primaite/simulator/file_system/file.py | 4 ++++ src/primaite/simulator/file_system/folder.py | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/primaite/simulator/file_system/file.py b/src/primaite/simulator/file_system/file.py index f1df87fb..52fe4f85 100644 --- a/src/primaite/simulator/file_system/file.py +++ b/src/primaite/simulator/file_system/file.py @@ -166,6 +166,10 @@ class File(FileSystemItemABC): if self.previous_hash is None: self.previous_hash = current_hash + # if the previous hash and current hash do not match, mark file as corrupted + if self.previous_hash is not current_hash: + self.corrupt() + return True def repair(self) -> bool: diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index 64b4a91d..9f176660 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -388,17 +388,17 @@ class Folder(FileSystemItemABC): return False # iterate through the files and run a check hash - # no_corrupted_files = True + no_corrupted_files = True for file_id in self.files: file = self.get_file_by_id(file_uuid=file_id) file.check_hash() - # if file.health_status == FileSystemItemHealthStatus.CORRUPT: - # no_corrupted_files = False + if file.health_status == FileSystemItemHealthStatus.CORRUPT: + no_corrupted_files = False # if one file in the folder is corrupted, set the folder status to corrupted - # if not no_corrupted_files: - # self.corrupt() + if not no_corrupted_files: + self.corrupt() self.sys_log.info(f"Checking hash of folder {self.name} (id: {self.uuid})") return True From 0fa9fa32bc0abd08b7cab3bda129707b75d9732e Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Thu, 25 Apr 2024 16:20:57 +0100 Subject: [PATCH 04/10] #2299: Add not implemented warning message. --- src/primaite/simulator/file_system/file.py | 6 +++++- src/primaite/simulator/file_system/folder.py | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/primaite/simulator/file_system/file.py b/src/primaite/simulator/file_system/file.py index 52fe4f85..a4d54e79 100644 --- a/src/primaite/simulator/file_system/file.py +++ b/src/primaite/simulator/file_system/file.py @@ -3,6 +3,7 @@ from __future__ import annotations import hashlib import json import os.path +import warnings from pathlib import Path from typing import Dict, Optional @@ -145,6 +146,10 @@ class File(FileSystemItemABC): Return False if corruption is detected, otherwise True """ + warnings.warn("NODE_FILE_CHECKHASH is currently not implemented.") + self.sys_log.warning("NODE_FILE_CHECKHASH is currently not implemented.") + return False + if self.deleted: self.sys_log.error(f"Unable to check hash of deleted file {self.folder_name}/{self.name}") return False @@ -169,7 +174,6 @@ class File(FileSystemItemABC): # if the previous hash and current hash do not match, mark file as corrupted if self.previous_hash is not current_hash: self.corrupt() - return True def repair(self) -> bool: diff --git a/src/primaite/simulator/file_system/folder.py b/src/primaite/simulator/file_system/folder.py index 9f176660..0e693a25 100644 --- a/src/primaite/simulator/file_system/folder.py +++ b/src/primaite/simulator/file_system/folder.py @@ -1,5 +1,6 @@ from __future__ import annotations +import warnings from typing import Dict, Optional from prettytable import MARKDOWN, PrettyTable @@ -383,6 +384,10 @@ class Folder(FileSystemItemABC): Return False if corruption is detected, otherwise True """ + warnings.warn("NODE_FOLDER_CHECKHASH is currently not implemented.") + self.sys_log.error("NODE_FOLDER_CHECKHASH is currently not implemented.") + return False + if self.deleted: self.sys_log.error(f"Unable to check hash of deleted folder {self.name}") return False From 0c375ebe4dbca20c95b5ed93c93f1c4f70ab647f Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Thu, 25 Apr 2024 17:08:03 +0100 Subject: [PATCH 05/10] #2299: Disable tests using CHECKHASH functionality --- .../unit_tests/_primaite/_simulator/_file_system/test_file.py | 4 ++++ .../_primaite/_simulator/_file_system/test_file_actions.py | 1 + .../_primaite/_simulator/_file_system/test_folder.py | 2 ++ .../_primaite/_simulator/_file_system/test_folder_actions.py | 1 + 4 files changed, 8 insertions(+) diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py index 32efe029..3c5e7989 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py @@ -1,3 +1,5 @@ +import pytest + from primaite.simulator.file_system.file import File from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus from primaite.simulator.file_system.file_type import FileType @@ -41,6 +43,7 @@ def test_file_reveal_to_red_scan(file_system): assert file.revealed_to_red is True +@pytest.mark.skip(reason="NODE_FILE_CHECKHASH not implemented") def test_simulated_file_check_hash(file_system): file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") @@ -52,6 +55,7 @@ def test_simulated_file_check_hash(file_system): assert file.health_status == FileSystemItemHealthStatus.CORRUPT +@pytest.mark.skip(reason="NODE_FILE_CHECKHASH not implemented") def test_real_file_check_hash(file_system): file: File = file_system.create_file(file_name="test_file.txt", real=True) diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py index 658b1b09..efa49134 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file_actions.py @@ -31,6 +31,7 @@ def test_file_scan_request(populated_file_system): assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT +@pytest.mark.skip(reason="NODE_FILE_CHECKHASH not implemented") def test_file_checkhash_request(populated_file_system): """Test that an agent can request a file hash check.""" fs, folder, file = populated_file_system diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py index bada2dab..e00c002d 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder.py @@ -119,6 +119,7 @@ def test_folder_corrupt_repair(file_system): assert file.health_status == FileSystemItemHealthStatus.GOOD +@pytest.mark.skip(reason="NODE_FILE_CHECKHASH not implemented") def test_simulated_folder_check_hash(file_system): folder: Folder = file_system.create_folder(folder_name="test_folder") file_system.create_file(file_name="test_file.txt", folder_name="test_folder") @@ -133,6 +134,7 @@ def test_simulated_folder_check_hash(file_system): assert folder.health_status == FileSystemItemHealthStatus.CORRUPT +@pytest.mark.skip(reason="NODE_FILE_CHECKHASH not implemented") def test_real_folder_check_hash(file_system): folder: Folder = file_system.create_folder(folder_name="test_folder") file_system.create_file(file_name="test_file.txt", folder_name="test_folder", real=True) diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py index 6e904f2a..0067e56c 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py @@ -49,6 +49,7 @@ def test_folder_scan_request(populated_file_system): assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT +@pytest.mark.skip(reason="NODE_FOLDER_CHECKHASH not implemented") def test_folder_checkhash_request(populated_file_system): """Test that an agent can request a folder hash check.""" fs, folder, file = populated_file_system From ec8b46f3bcb2b651f204141abc5952b3ded02f39 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Thu, 25 Apr 2024 17:56:55 +0100 Subject: [PATCH 06/10] #2299: Add tests to check for 'not implemented' warning message. --- .../_primaite/_simulator/_file_system/test_file.py | 13 +++++++++++++ .../_simulator/_file_system/test_folder_actions.py | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py index 3c5e7989..9d9228d8 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_file.py @@ -1,3 +1,5 @@ +import warnings + import pytest from primaite.simulator.file_system.file import File @@ -84,3 +86,14 @@ def test_file_corrupt_repair_restore(file_system): file.restore() assert file.health_status == FileSystemItemHealthStatus.GOOD + + +def test_file_warning_triggered(file_system): + file: File = file_system.create_file(file_name="test_file.txt", folder_name="test_folder") + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + file.check_hash() + # Check warning issued + assert len(w) == 1 + assert "not implemented" in str(w[-1].message) diff --git a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py index 0067e56c..11043844 100644 --- a/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_file_system/test_folder_actions.py @@ -1,3 +1,4 @@ +import warnings from typing import Tuple import pytest @@ -63,6 +64,16 @@ def test_folder_checkhash_request(populated_file_system): assert folder.health_status == FileSystemItemHealthStatus.CORRUPT +def test_folder_warning_triggered(populated_file_system): + fs, folder, _ = populated_file_system + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + fs.apply_request(request=["folder", folder.name, "checkhash"]) + # Check warning issued + assert len(w) == 1 + assert "not implemented" in str(w[-1].message) + + def test_folder_repair_request(populated_file_system): """Test that an agent can request a folder repair.""" fs, folder, file = populated_file_system From 75f404416cd4220e4237cbe267caa578ab3a00a5 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Mon, 29 Apr 2024 10:30:47 +0100 Subject: [PATCH 07/10] #2299: Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6932739..212404b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Order of service health state - Starting a node didn't start the services on it - Fixed an issue where the services were still able to run even though the node the service is installed on is turned off - +- Deprecated the use of NODE_FILE_CHECKHASH and NODE_FOLDER_CHECKHASH in the current release. ### Added From e7db5c7f581cf76c746ea145aad611f025434410 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Mon, 29 Apr 2024 15:30:24 +0100 Subject: [PATCH 08/10] #2299: Review comments --- CHANGELOG.md | 2 +- src/primaite/config/_package_data/data_manipulation.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 212404b7..301717a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Order of service health state - Starting a node didn't start the services on it - Fixed an issue where the services were still able to run even though the node the service is installed on is turned off -- Deprecated the use of NODE_FILE_CHECKHASH and NODE_FOLDER_CHECKHASH in the current release. +- The use of NODE_FILE_CHECKHASH and NODE_FOLDER_CHECKHASH in the current release is marked as 'Not Implemented'. ### Added diff --git a/src/primaite/config/_package_data/data_manipulation.yaml b/src/primaite/config/_package_data/data_manipulation.yaml index 8c365320..8b7e07f4 100644 --- a/src/primaite/config/_package_data/data_manipulation.yaml +++ b/src/primaite/config/_package_data/data_manipulation.yaml @@ -2,7 +2,7 @@ io_settings: save_agent_actions: true save_step_metadata: false save_pcap_logs: false - save_sys_logs: false + save_sys_logs: true game: @@ -312,7 +312,7 @@ agents: folder_id: 0 file_id: 0 10: - action: "NODE_FILE_SCAN" # CHECKHASH replaced by SCAN - but the behaviour is the same in this context. + action: "NODE_FILE_CHECKHASH" # CHECKHASH replaced by SCAN - but the behaviour is the same in this context. options: node_id: 2 folder_id: 0 @@ -340,7 +340,7 @@ agents: node_id: 2 folder_id: 0 15: - action: "NODE_FOLDER_SCAN" # CHECKHASH replaced by SCAN - but the behaviour is the same in this context. + action: "NODE_FOLDER_CHECKHASH" # CHECKHASH replaced by SCAN - but the behaviour is the same in this context. options: node_id: 2 folder_id: 0 From e667223a989eb93702bbaeb3786696e170a31fd1 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Mon, 29 Apr 2024 15:34:23 +0100 Subject: [PATCH 09/10] #2299: Remove debug cell from notebook. --- .../Data-Manipulation-E2E-Demonstration.ipynb | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb index 43873662..deb6505f 100644 --- a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb @@ -410,35 +410,6 @@ "pprint(obs)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test NODE_FOLDER_CHECKHASH\n", - "res = env.game.simulation.network.get_node_by_hostname('database_server').file_system.get_folder(folder_name = 'database').health_status\n", - "print(f'Folder status = {res}')\n", - "obs, reward, terminated, truncated, info = env.step(15)\n", - "obs, reward, terminated, truncated, info = env.step(14) # scan database folder\n", - "\n", - "res = env.game.simulation.network.get_node_by_hostname('database_server').file_system.get_folder(folder_name = 'database').health_status\n", - "print(f'Folder status = {res}')\n", - "\n", - "\n", - "\n", - "# Test NODE_FILE_CHECKHASH\n", - "res = env.game.simulation.network.get_node_by_hostname('database_server').file_system.get_file(folder_name = 'database', file_name = 'database.db').health_status\n", - "print(f'File status = {res}')\n", - "obs, reward, terminated, truncated, info = env.step(10)\n", - "obs, reward, terminated, truncated, info = env.step(9) # scan database file\n", - "\n", - "res = env.game.simulation.network.get_node_by_hostname('database_server').file_system.get_file(folder_name = 'database', file_name = 'database.db').health_status\n", - "print(f'File status = {res}')\n", - "\n", - "# pprint(obs['NODES'])\n" - ] - }, { "cell_type": "markdown", "metadata": {}, From 3d0ebe1aba7ceab4e2dace3018fbdd6d4aa99429 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Tue, 30 Apr 2024 10:39:33 +0100 Subject: [PATCH 10/10] #2299: Correct possible mis-merge. --- .../notebooks/Data-Manipulation-E2E-Demonstration.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb index deb6505f..2a5ec16f 100644 --- a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb @@ -639,7 +639,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, the reward will eventually increase to 1.0, even after red agent attempts subsequent attacks." + "Now, the reward will eventually increase to 0.9, even after red agent attempts subsequent attacks." ] }, { @@ -648,7 +648,7 @@ "metadata": {}, "outputs": [], "source": [ - "for step in range(30):\n", + "for step in range(40):\n", " obs, reward, terminated, truncated, info = env.step(0) # do nothing\n", " print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker'].action}, Blue reward:{reward:.2f}\" )" ] @@ -686,7 +686,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.10.11" } }, "nbformat": 4,