From 600dc3f016b3c18add5c5f109b7ad0fe24c154ad Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Mon, 25 Mar 2024 16:58:27 +0000 Subject: [PATCH 1/7] #2404 add application scan, close, and fix actions, fix and enable service scan test --- src/primaite/game/agent/actions.py | 27 ++++++ .../system/applications/application.py | 14 +++ tests/conftest.py | 3 + .../game_layer/test_actions.py | 89 ++++++++++++++++++- 4 files changed, 129 insertions(+), 4 deletions(-) diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index af90c1e1..bdb90b57 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -195,6 +195,30 @@ class NodeApplicationExecuteAction(NodeApplicationAbstractAction): self.verb: str = "execute" +class NodeApplicationScanAction(NodeApplicationAbstractAction): + """Action which scans an application.""" + + def __init__(self, manager: "ActionManager", num_nodes: int, num_applications: int, **kwargs) -> None: + super().__init__(manager=manager, num_nodes=num_nodes, num_applications=num_applications) + self.verb: str = "scan" + + +class NodeApplicationCloseAction(NodeApplicationAbstractAction): + """Action which closes an application.""" + + def __init__(self, manager: "ActionManager", num_nodes: int, num_applications: int, **kwargs) -> None: + super().__init__(manager=manager, num_nodes=num_nodes, num_applications=num_applications) + self.verb: str = "close" + + +class NodeApplicationFixAction(NodeApplicationAbstractAction): + """Action which fixes an application.""" + + def __init__(self, manager: "ActionManager", num_nodes: int, num_applications: int, **kwargs) -> None: + super().__init__(manager=manager, num_nodes=num_nodes, num_applications=num_applications) + self.verb: str = "patch" + + class NodeFolderAbstractAction(AbstractAction): """ Base class for folder actions. @@ -631,6 +655,9 @@ class ActionManager: "NODE_SERVICE_ENABLE": NodeServiceEnableAction, "NODE_SERVICE_PATCH": NodeServicePatchAction, "NODE_APPLICATION_EXECUTE": NodeApplicationExecuteAction, + "NODE_APPLICATION_SCAN": NodeApplicationScanAction, + "NODE_APPLICATION_CLOSE": NodeApplicationCloseAction, + "NODE_APPLICATION_FIX": NodeApplicationFixAction, "NODE_FILE_SCAN": NodeFileScanAction, "NODE_FILE_CHECKHASH": NodeFileCheckhashAction, "NODE_FILE_DELETE": NodeFileDeleteAction, diff --git a/src/primaite/simulator/system/applications/application.py b/src/primaite/simulator/system/applications/application.py index b7422680..4ea893e0 100644 --- a/src/primaite/simulator/system/applications/application.py +++ b/src/primaite/simulator/system/applications/application.py @@ -3,6 +3,8 @@ from enum import Enum from typing import Any, Dict, Set from primaite import getLogger +from primaite.interface.request import RequestResponse +from primaite.simulator.core import RequestManager, RequestType from primaite.simulator.system.software import IOSoftware, SoftwareHealthState _LOGGER = getLogger(__name__) @@ -38,6 +40,17 @@ class Application(IOSoftware): def __init__(self, **kwargs): super().__init__(**kwargs) + def _init_request_manager(self) -> RequestManager: + """ + Initialise the request manager. + + More information in user guide and docstring for SimComponent._init_request_manager. + """ + rm = super()._init_request_manager() + + rm.add_request("close", RequestType(func=lambda request, context: RequestResponse.from_bool(self.close()))) + return rm + @abstractmethod def describe_state(self) -> Dict: """ @@ -109,6 +122,7 @@ class Application(IOSoftware): if self.operating_state == ApplicationOperatingState.RUNNING: self.sys_log.info(f"Closed Application{self.name}") self.operating_state = ApplicationOperatingState.CLOSED + return True def install(self) -> None: """Install Application.""" diff --git a/tests/conftest.py b/tests/conftest.py index fbfd23f2..9eaf1782 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -477,6 +477,9 @@ def game_and_agent(): {"type": "NODE_SERVICE_ENABLE"}, {"type": "NODE_SERVICE_PATCH"}, {"type": "NODE_APPLICATION_EXECUTE"}, + {"type": "NODE_APPLICATION_SCAN"}, + {"type": "NODE_APPLICATION_CLOSE"}, + {"type": "NODE_APPLICATION_FIX"}, {"type": "NODE_FILE_SCAN"}, {"type": "NODE_FILE_CHECKHASH"}, {"type": "NODE_FILE_DELETE"}, diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index 5ced802c..98e6ea5d 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -17,6 +17,7 @@ import pytest from primaite.game.agent.interface import ProxyAgent from primaite.game.game import PrimaiteGame from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.system.applications.application import ApplicationOperatingState from primaite.simulator.system.applications.web_browser import WebBrowser from primaite.simulator.system.software import SoftwareHealthState @@ -30,7 +31,6 @@ def test_do_nothing_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]) game.step() -@pytest.mark.skip(reason="Waiting to merge ticket 2166") def test_node_service_scan_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): """ Test that the NodeServiceScanAction can form a request and that it is accepted by the simulation. @@ -42,12 +42,12 @@ def test_node_service_scan_integration(game_and_agent: Tuple[PrimaiteGame, Proxy game, agent = game_and_agent # 1: Check that the service starts off in a good state, and that visible state is hidden until first scan - svc = game.simulation.network.get_node_by_hostname("client_1").software_manager.software.get("DNSClient") + svc = game.simulation.network.get_node_by_hostname("server_1").software_manager.software.get("DNSServer") assert svc.health_state_actual == SoftwareHealthState.GOOD assert svc.health_state_visible == SoftwareHealthState.UNUSED # 2: Scan and check that the visible state is now correct - action = ("NODE_SERVICE_SCAN", {"node_id": 0, "service_id": 0}) + action = ("NODE_SERVICE_SCAN", {"node_id": 1, "service_id": 0}) agent.store_action(action) game.step() assert svc.health_state_actual == SoftwareHealthState.GOOD @@ -58,7 +58,7 @@ def test_node_service_scan_integration(game_and_agent: Tuple[PrimaiteGame, Proxy assert svc.health_state_visible == SoftwareHealthState.GOOD # 4: Scan and check that the visible state is now correct - action = ("NODE_SERVICE_SCAN", {"node_id": 0, "service_id": 0}) + action = ("NODE_SERVICE_SCAN", {"node_id": 1, "service_id": 0}) agent.store_action(action) game.step() assert svc.health_state_actual == SoftwareHealthState.COMPROMISED @@ -374,3 +374,84 @@ def test_network_router_port_enable_integration(game_and_agent: Tuple[PrimaiteGa # 3: Check that the Port is enabled, and that client 1 can ping again assert router.network_interface[1].enabled == True assert client_1.ping("10.0.2.3") + + +def test_node_application_scan_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): + """Test that the NodeApplicationScanAction updates the application status as expected.""" + game, agent = game_and_agent + + # 1: Check that http traffic is going across the network nicely. + client_1 = game.simulation.network.get_node_by_hostname("client_1") + + browser: WebBrowser = client_1.software_manager.software.get("WebBrowser") + browser.run() + browser.target_url = "http://www.example.com" + assert browser.get_webpage() # check that the browser can access example.com + + assert browser.health_state_actual == SoftwareHealthState.GOOD + assert browser.health_state_visible == SoftwareHealthState.UNUSED + + # 2: Scan and check that the visible state is now correct + action = ("NODE_APPLICATION_SCAN", {"node_id": 0, "application_id": 0}) + agent.store_action(action) + game.step() + assert browser.health_state_actual == SoftwareHealthState.GOOD + assert browser.health_state_visible == SoftwareHealthState.GOOD + + # 3: Corrupt the service and check that the visible state is still good + browser.health_state_actual = SoftwareHealthState.COMPROMISED + assert browser.health_state_visible == SoftwareHealthState.GOOD + + # 4: Scan and check that the visible state is now correct + action = ("NODE_APPLICATION_SCAN", {"node_id": 0, "application_id": 0}) + agent.store_action(action) + game.step() + assert browser.health_state_actual == SoftwareHealthState.COMPROMISED + assert browser.health_state_visible == SoftwareHealthState.COMPROMISED + + +def test_node_application_fix_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): + """Test that the NodeApplicationFixAction can form a request and that it is accepted by the simulation. + + When you initiate a fix action, the software health state turns to PATCHING, then after a few steps, it goes + to GOOD.""" + game, agent = game_and_agent + + # 1: Check that http traffic is going across the network nicely. + client_1 = game.simulation.network.get_node_by_hostname("client_1") + + browser: WebBrowser = client_1.software_manager.software.get("WebBrowser") + browser.health_state_actual = SoftwareHealthState.COMPROMISED + + # 2: Apply a fix action + action = ("NODE_APPLICATION_FIX", {"node_id": 0, "application_id": 0}) + agent.store_action(action) + game.step() + + # 3: Check that the application is now in the patching state + assert browser.health_state_actual == SoftwareHealthState.PATCHING + + # 4: perform a few do-nothing steps and check that the application is now in the good state + action = ("DONOTHING", {}) + agent.store_action(action) + game.step() + assert browser.health_state_actual == SoftwareHealthState.GOOD + + +def test_node_application_close_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): + """Test that the NodeApplicationCloseAction can form a request and that it is accepted by the simulation. + + When you initiate a close action, the Application Operating State changes for CLOSED.""" + game, agent = game_and_agent + client_1 = game.simulation.network.get_node_by_hostname("client_1") + + browser: WebBrowser = client_1.software_manager.software.get("WebBrowser") + browser.run() + assert browser.operating_state == ApplicationOperatingState.RUNNING + + # 2: Apply a close action + action = ("NODE_APPLICATION_CLOSE", {"node_id": 0, "application_id": 0}) + agent.store_action(action) + game.step() + + assert browser.operating_state == ApplicationOperatingState.CLOSED From 944b248300fc6729e21cc9446f1682207e3847be Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Tue, 26 Mar 2024 10:51:33 +0000 Subject: [PATCH 2/7] #2404 rename software patch to fix --- .../config/benchmark_training_config.yaml | 2 +- .../v2.0.0/v2.0.0_benchmark_metadata.json | 2 +- diagram/classes.puml | 8 ++--- .../_package_data/data_manipulation.yaml | 4 +-- .../_package_data/data_manipulation_marl.yaml | 8 ++--- src/primaite/game/agent/actions.py | 10 +++--- .../services/database/database_service.py | 6 ++-- src/primaite/simulator/system/software.py | 32 +++++++++---------- .../assets/configs/bad_primaite_session.yaml | 4 +-- .../configs/eval_only_primaite_session.yaml | 4 +-- tests/assets/configs/multi_agent_session.yaml | 8 ++--- tests/assets/configs/shared_rewards.yaml | 4 +-- .../assets/configs/test_primaite_session.yaml | 4 +-- .../configs/train_only_primaite_session.yaml | 4 +-- .../session_metadata.json | 2 +- tests/conftest.py | 2 +- .../game_layer/test_actions.py | 6 ++-- .../test_simulation/test_request_response.py | 4 +-- .../_applications/test_applications.py | 2 +- .../_applications/test_database_client.py | 2 -- .../_system/_services/test_service_actions.py | 6 ++-- .../_system/_services/test_services.py | 6 ++-- 22 files changed, 64 insertions(+), 66 deletions(-) diff --git a/benchmark/config/benchmark_training_config.yaml b/benchmark/config/benchmark_training_config.yaml index 02b6377c..17811586 100644 --- a/benchmark/config/benchmark_training_config.yaml +++ b/benchmark/config/benchmark_training_config.yaml @@ -158,7 +158,7 @@ green_ier_blocked: -0.001 # Patching / Reset durations os_patching_duration: 5 # The time taken to patch the OS node_reset_duration: 5 # The time taken to reset a node (hardware) -service_patching_duration: 5 # The time taken to patch a service +service_fixing_duration: 5 # The time taken to patch a service file_system_repairing_limit: 5 # The time take to repair the file system file_system_restoring_limit: 5 # The time take to restore the file system file_system_scanning_limit: 5 # The time taken to scan the file system diff --git a/benchmark/results/v2.0.0/v2.0.0_benchmark_metadata.json b/benchmark/results/v2.0.0/v2.0.0_benchmark_metadata.json index b2745967..3ed57745 100644 --- a/benchmark/results/v2.0.0/v2.0.0_benchmark_metadata.json +++ b/benchmark/results/v2.0.0/v2.0.0_benchmark_metadata.json @@ -5634,7 +5634,7 @@ "green_ier_blocked": -0.001, "os_patching_duration": 5, "node_reset_duration": 5, - "service_patching_duration": 5, + "service_fixing_duration": 5, "file_system_repairing_limit": 5, "file_system_restoring_limit": 5, "file_system_scanning_limit": 5 diff --git a/diagram/classes.puml b/diagram/classes.puml index 4505f3e0..71e0b0b1 100644 --- a/diagram/classes.puml +++ b/diagram/classes.puml @@ -48,7 +48,7 @@ class "ActiveNode" as primaite.nodes.active_node.ActiveNode { file_system_state_actual : GOOD file_system_state_observed : REPAIRING, RESTORING, GOOD ip_address : str - patching_count : int + fixing_count : int software_state software_state : GOOD set_file_system_state(file_system_state: FileSystemState) -> None @@ -353,10 +353,10 @@ class "SB3Agent" as primaite.agents.sb3.SB3Agent { } class "Service" as primaite.common.service.Service { name : str - patching_count : int + fixing_count : int port : str software_state : GOOD - reduce_patching_count() -> None + reduce_fixing_count() -> None } class "ServiceNode" as primaite.nodes.service_node.ServiceNode { services : Dict[str, Service] @@ -455,7 +455,7 @@ class "TrainingConfig" as primaite.config.training_config.TrainingConfig { sb3_output_verbose_level scanning : float seed : Optional[int] - service_patching_duration : int + service_fixing_duration : int session_type time_delay : int from_dict(config_dict: Dict[str, Any]) -> TrainingConfig diff --git a/src/primaite/config/_package_data/data_manipulation.yaml b/src/primaite/config/_package_data/data_manipulation.yaml index c561030a..12f60b63 100644 --- a/src/primaite/config/_package_data/data_manipulation.yaml +++ b/src/primaite/config/_package_data/data_manipulation.yaml @@ -244,7 +244,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -339,7 +339,7 @@ agents: folder_id: 0 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 diff --git a/src/primaite/config/_package_data/data_manipulation_marl.yaml b/src/primaite/config/_package_data/data_manipulation_marl.yaml index 85d282ba..b632f626 100644 --- a/src/primaite/config/_package_data/data_manipulation_marl.yaml +++ b/src/primaite/config/_package_data/data_manipulation_marl.yaml @@ -246,7 +246,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -341,7 +341,7 @@ agents: folder_id: 0 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 @@ -797,7 +797,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -892,7 +892,7 @@ agents: folder_id: 0 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index bdb90b57..b79fc985 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -156,12 +156,12 @@ class NodeServiceEnableAction(NodeServiceAbstractAction): self.verb: str = "enable" -class NodeServicePatchAction(NodeServiceAbstractAction): - """Action which patches a service.""" +class NodeServiceFixAction(NodeServiceAbstractAction): + """Action which fixes a service.""" def __init__(self, manager: "ActionManager", num_nodes: int, num_services: int, **kwargs) -> None: super().__init__(manager=manager, num_nodes=num_nodes, num_services=num_services) - self.verb: str = "patch" + self.verb: str = "fix" class NodeApplicationAbstractAction(AbstractAction): @@ -216,7 +216,7 @@ class NodeApplicationFixAction(NodeApplicationAbstractAction): def __init__(self, manager: "ActionManager", num_nodes: int, num_applications: int, **kwargs) -> None: super().__init__(manager=manager, num_nodes=num_nodes, num_applications=num_applications) - self.verb: str = "patch" + self.verb: str = "fix" class NodeFolderAbstractAction(AbstractAction): @@ -653,7 +653,7 @@ class ActionManager: "NODE_SERVICE_RESTART": NodeServiceRestartAction, "NODE_SERVICE_DISABLE": NodeServiceDisableAction, "NODE_SERVICE_ENABLE": NodeServiceEnableAction, - "NODE_SERVICE_PATCH": NodeServicePatchAction, + "NODE_SERVICE_FIX": NodeServiceFixAction, "NODE_APPLICATION_EXECUTE": NodeApplicationExecuteAction, "NODE_APPLICATION_SCAN": NodeApplicationScanAction, "NODE_APPLICATION_CLOSE": NodeApplicationCloseAction, diff --git a/src/primaite/simulator/system/services/database/database_service.py b/src/primaite/simulator/system/services/database/database_service.py index c73132eb..411359a2 100644 --- a/src/primaite/simulator/system/services/database/database_service.py +++ b/src/primaite/simulator/system/services/database/database_service.py @@ -304,8 +304,8 @@ class DatabaseService(Service): self.backup_database() return super().apply_timestep(timestep) - def _update_patch_status(self) -> None: + def _update_fix_status(self) -> None: """Perform a database restore when the patching countdown is finished.""" - super()._update_patch_status() - if self._patching_countdown is None: + super()._update_fix_status() + if self._fixing_countdown is None: self.restore_backup() diff --git a/src/primaite/simulator/system/software.py b/src/primaite/simulator/system/software.py index d55f141f..9b54f802 100644 --- a/src/primaite/simulator/system/software.py +++ b/src/primaite/simulator/system/software.py @@ -82,7 +82,7 @@ class Software(SimComponent): "The health state of the software visible to the red agent." criticality: SoftwareCriticality = SoftwareCriticality.LOWEST "The criticality level of the software." - patching_count: int = 0 + fixing_count: int = 0 "The count of patches applied to the software, defaults to 0." scanning_count: int = 0 "The count of times the software has been scanned, defaults to 0." @@ -96,9 +96,9 @@ class Software(SimComponent): "The FileSystem of the Node the Software is installed on." folder: Optional[Folder] = None "The folder on the file system the Software uses." - patching_duration: int = 2 + fixing_duration: int = 2 "The number of ticks it takes to patch the software." - _patching_countdown: Optional[int] = None + _fixing_countdown: Optional[int] = None "Current number of ticks left to patch the software." def _init_request_manager(self) -> RequestManager: @@ -117,9 +117,9 @@ class Software(SimComponent): ), ) rm.add_request( - "patch", + "fix", RequestType( - func=lambda request, context: RequestResponse.from_bool(self.patch()), + func=lambda request, context: RequestResponse.from_bool(self.fix()), ), ) rm.add_request("scan", RequestType(func=lambda request, context: RequestResponse.from_bool(self.scan()))) @@ -149,7 +149,7 @@ class Software(SimComponent): "health_state_actual": self.health_state_actual.value, "health_state_visible": self.health_state_visible.value, "criticality": self.criticality.value, - "patching_count": self.patching_count, + "fixing_count": self.fixing_count, "scanning_count": self.scanning_count, "revealed_to_red": self.revealed_to_red, } @@ -194,21 +194,21 @@ class Software(SimComponent): self.health_state_visible = self.health_state_actual return True - def patch(self) -> bool: - """Perform a patch on the software.""" + def fix(self) -> bool: + """Perform a fix on the software.""" if self.health_state_actual in (SoftwareHealthState.COMPROMISED, SoftwareHealthState.GOOD): - self._patching_countdown = self.patching_duration + self._fixing_countdown = self.fixing_duration self.set_health_state(SoftwareHealthState.PATCHING) return True return False - def _update_patch_status(self) -> None: - """Update the patch status of the software.""" - self._patching_countdown -= 1 - if self._patching_countdown <= 0: + def _update_fix_status(self) -> None: + """Update the fix status of the software.""" + self._fixing_countdown -= 1 + if self._fixing_countdown <= 0: self.set_health_state(SoftwareHealthState.GOOD) - self._patching_countdown = None - self.patching_count += 1 + self._fixing_countdown = None + self.fixing_count += 1 def reveal_to_red(self) -> None: """Reveals the software to the red agent.""" @@ -222,7 +222,7 @@ class Software(SimComponent): """ super().apply_timestep(timestep) if self.health_state_actual == SoftwareHealthState.PATCHING: - self._update_patch_status() + self._update_fix_status() class IOSoftware(Software): diff --git a/tests/assets/configs/bad_primaite_session.yaml b/tests/assets/configs/bad_primaite_session.yaml index 38d54ce3..e599ee7e 100644 --- a/tests/assets/configs/bad_primaite_session.yaml +++ b/tests/assets/configs/bad_primaite_session.yaml @@ -155,7 +155,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -250,7 +250,7 @@ agents: folder_id: 1 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 diff --git a/tests/assets/configs/eval_only_primaite_session.yaml b/tests/assets/configs/eval_only_primaite_session.yaml index f2815578..9d1404d8 100644 --- a/tests/assets/configs/eval_only_primaite_session.yaml +++ b/tests/assets/configs/eval_only_primaite_session.yaml @@ -159,7 +159,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -254,7 +254,7 @@ agents: folder_id: 1 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 diff --git a/tests/assets/configs/multi_agent_session.yaml b/tests/assets/configs/multi_agent_session.yaml index 8bbddb76..acb62c96 100644 --- a/tests/assets/configs/multi_agent_session.yaml +++ b/tests/assets/configs/multi_agent_session.yaml @@ -166,7 +166,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -261,7 +261,7 @@ agents: folder_id: 1 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 @@ -610,7 +610,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -705,7 +705,7 @@ agents: folder_id: 1 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 diff --git a/tests/assets/configs/shared_rewards.yaml b/tests/assets/configs/shared_rewards.yaml index daffa585..10feba9d 100644 --- a/tests/assets/configs/shared_rewards.yaml +++ b/tests/assets/configs/shared_rewards.yaml @@ -244,7 +244,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -339,7 +339,7 @@ agents: folder_id: 0 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 diff --git a/tests/assets/configs/test_primaite_session.yaml b/tests/assets/configs/test_primaite_session.yaml index 121cc7f1..a8b33032 100644 --- a/tests/assets/configs/test_primaite_session.yaml +++ b/tests/assets/configs/test_primaite_session.yaml @@ -169,7 +169,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -264,7 +264,7 @@ agents: folder_id: 1 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 diff --git a/tests/assets/configs/train_only_primaite_session.yaml b/tests/assets/configs/train_only_primaite_session.yaml index 71a23989..d0cbaab3 100644 --- a/tests/assets/configs/train_only_primaite_session.yaml +++ b/tests/assets/configs/train_only_primaite_session.yaml @@ -167,7 +167,7 @@ agents: - type: NODE_SERVICE_RESTART - type: NODE_SERVICE_DISABLE - type: NODE_SERVICE_ENABLE - - type: NODE_SERVICE_PATCH + - type: NODE_SERVICE_FIX - type: NODE_FILE_SCAN - type: NODE_FILE_CHECKHASH - type: NODE_FILE_DELETE @@ -262,7 +262,7 @@ agents: folder_id: 1 file_id: 0 13: - action: "NODE_SERVICE_PATCH" + action: "NODE_SERVICE_FIX" options: node_id: 2 service_id: 0 diff --git a/tests/assets/example_sb3_agent_session/session_metadata.json b/tests/assets/example_sb3_agent_session/session_metadata.json index c0968ba7..085e20cc 100644 --- a/tests/assets/example_sb3_agent_session/session_metadata.json +++ b/tests/assets/example_sb3_agent_session/session_metadata.json @@ -1 +1 @@ -{ "uuid": "301874d3-2e14-43c2-ba7f-e2b03ad05dde", "start_datetime": "2023-07-14T09:48:22.973005", "end_datetime": "2023-07-14T09:48:34.182715", "learning": { "total_episodes": 10, "total_time_steps": 2560 }, "evaluation": { "total_episodes": 5, "total_time_steps": 1280 }, "env": { "training_config": { "agent_framework": "SB3", "deep_learning_framework": "TF2", "agent_identifier": "PPO", "hard_coded_agent_view": "FULL", "random_red_agent": false, "action_type": "NODE", "num_train_episodes": 10, "num_train_steps": 256, "num_eval_episodes": 5, "num_eval_steps": 256, "checkpoint_every_n_episodes": 10, "observation_space": { "components": [ { "name": "NODE_LINK_TABLE" } ] }, "time_delay": 5, "session_type": "TRAIN_EVAL", "load_agent": false, "agent_load_file": null, "observation_space_high_value": 1000000000, "sb3_output_verbose_level": "NONE", "all_ok": 0, "off_should_be_on": -0.001, "off_should_be_resetting": -0.0005, "on_should_be_off": -0.0002, "on_should_be_resetting": -0.0005, "resetting_should_be_on": -0.0005, "resetting_should_be_off": -0.0002, "resetting": -0.0003, "good_should_be_patching": 0.0002, "good_should_be_compromised": 0.0005, "good_should_be_overwhelmed": 0.0005, "patching_should_be_good": -0.0005, "patching_should_be_compromised": 0.0002, "patching_should_be_overwhelmed": 0.0002, "patching": -0.0003, "compromised_should_be_good": -0.002, "compromised_should_be_patching": -0.002, "compromised_should_be_overwhelmed": -0.002, "compromised": -0.002, "overwhelmed_should_be_good": -0.002, "overwhelmed_should_be_patching": -0.002, "overwhelmed_should_be_compromised": -0.002, "overwhelmed": -0.002, "good_should_be_repairing": 0.0002, "good_should_be_restoring": 0.0002, "good_should_be_corrupt": 0.0005, "good_should_be_destroyed": 0.001, "repairing_should_be_good": -0.0005, "repairing_should_be_restoring": 0.0002, "repairing_should_be_corrupt": 0.0002, "repairing_should_be_destroyed": 0.0, "repairing": -0.0003, "restoring_should_be_good": -0.001, "restoring_should_be_repairing": -0.0002, "restoring_should_be_corrupt": 0.0001, "restoring_should_be_destroyed": 0.0002, "restoring": -0.0006, "corrupt_should_be_good": -0.001, "corrupt_should_be_repairing": -0.001, "corrupt_should_be_restoring": -0.001, "corrupt_should_be_destroyed": 0.0002, "corrupt": -0.001, "destroyed_should_be_good": -0.002, "destroyed_should_be_repairing": -0.002, "destroyed_should_be_restoring": -0.002, "destroyed_should_be_corrupt": -0.002, "destroyed": -0.002, "scanning": -0.0002, "red_ier_running": -0.0005, "green_ier_blocked": -0.001, "os_patching_duration": 5, "node_reset_duration": 5, "node_booting_duration": 3, "node_shutdown_duration": 2, "service_patching_duration": 5, "file_system_repairing_limit": 5, "file_system_restoring_limit": 5, "file_system_scanning_limit": 5, "deterministic": true, "seed": 12345 }, "lay_down_config": [ { "item_type": "PORTS", "ports_list": [ { "port": "80" } ] }, { "item_type": "SERVICES", "service_list": [ { "name": "TCP" } ] }, { "item_type": "NODE", "node_id": "1", "name": "PC1", "node_class": "SERVICE", "node_type": "COMPUTER", "priority": "P5", "hardware_state": "ON", "ip_address": "192.168.1.2", "software_state": "GOOD", "file_system_state": "GOOD", "services": [ { "name": "TCP", "port": "80", "state": "GOOD" } ] }, { "item_type": "NODE", "node_id": "2", "name": "PC2", "node_class": "SERVICE", "node_type": "COMPUTER", "priority": "P5", "hardware_state": "ON", "ip_address": "192.168.1.3", "software_state": "GOOD", "file_system_state": "GOOD", "services": [ { "name": "TCP", "port": "80", "state": "GOOD" } ] }, { "item_type": "NODE", "node_id": "3", "name": "SWITCH1", "node_class": "ACTIVE", "node_type": "SWITCH", "priority": "P2", "hardware_state": "ON", "ip_address": "192.168.1.1", "software_state": "GOOD", "file_system_state": "GOOD" }, { "item_type": "NODE", "node_id": "4", "name": "SERVER1", "node_class": "SERVICE", "node_type": "SERVER", "priority": "P5", "hardware_state": "ON", "ip_address": "192.168.1.4", "software_state": "GOOD", "file_system_state": "GOOD", "services": [ { "name": "TCP", "port": "80", "state": "GOOD" } ] }, { "item_type": "LINK", "id": "5", "name": "link1", "bandwidth": 1000000000, "source": "1", "destination": "3" }, { "item_type": "LINK", "id": "6", "name": "link2", "bandwidth": 1000000000, "source": "2", "destination": "3" }, { "item_type": "LINK", "id": "7", "name": "link3", "bandwidth": 1000000000, "source": "3", "destination": "4" }, { "item_type": "GREEN_IER", "id": "8", "start_step": 1, "end_step": 256, "load": 10000, "protocol": "TCP", "port": "80", "source": "1", "destination": "4", "mission_criticality": 1 }, { "item_type": "GREEN_IER", "id": "9", "start_step": 1, "end_step": 256, "load": 10000, "protocol": "TCP", "port": "80", "source": "2", "destination": "4", "mission_criticality": 1 }, { "item_type": "GREEN_IER", "id": "10", "start_step": 1, "end_step": 256, "load": 10000, "protocol": "TCP", "port": "80", "source": "4", "destination": "2", "mission_criticality": 5 }, { "item_type": "ACL_RULE", "id": "11", "permission": "ALLOW", "source": "192.168.1.2", "destination": "192.168.1.4", "protocol": "TCP", "port": 80, "position": 0 }, { "item_type": "ACL_RULE", "id": "12", "permission": "ALLOW", "source": "192.168.1.3", "destination": "192.168.1.4", "protocol": "TCP", "port": 80, "position": 1 }, { "item_type": "ACL_RULE", "id": "13", "permission": "ALLOW", "source": "192.168.1.4", "destination": "192.168.1.3", "protocol": "TCP", "port": 80, "position": 2 }, { "item_type": "RED_POL", "id": "14", "start_step": 20, "end_step": 20, "targetNodeId": "1", "initiator": "DIRECT", "type": "SERVICE", "protocol": "TCP", "state": "COMPROMISED", "sourceNodeId": "NA", "sourceNodeService": "NA", "sourceNodeServiceState": "NA" }, { "item_type": "RED_IER", "id": "15", "start_step": 30, "end_step": 256, "load": 10000000, "protocol": "TCP", "port": "80", "source": "1", "destination": "4", "mission_criticality": 0 }, { "item_type": "RED_POL", "id": "16", "start_step": 40, "end_step": 40, "targetNodeId": "4", "initiator": "IER", "type": "SERVICE", "protocol": "TCP", "state": "OVERWHELMED", "sourceNodeId": "NA", "sourceNodeService": "NA", "sourceNodeServiceState": "NA" } ] } } +{ "uuid": "301874d3-2e14-43c2-ba7f-e2b03ad05dde", "start_datetime": "2023-07-14T09:48:22.973005", "end_datetime": "2023-07-14T09:48:34.182715", "learning": { "total_episodes": 10, "total_time_steps": 2560 }, "evaluation": { "total_episodes": 5, "total_time_steps": 1280 }, "env": { "training_config": { "agent_framework": "SB3", "deep_learning_framework": "TF2", "agent_identifier": "PPO", "hard_coded_agent_view": "FULL", "random_red_agent": false, "action_type": "NODE", "num_train_episodes": 10, "num_train_steps": 256, "num_eval_episodes": 5, "num_eval_steps": 256, "checkpoint_every_n_episodes": 10, "observation_space": { "components": [ { "name": "NODE_LINK_TABLE" } ] }, "time_delay": 5, "session_type": "TRAIN_EVAL", "load_agent": false, "agent_load_file": null, "observation_space_high_value": 1000000000, "sb3_output_verbose_level": "NONE", "all_ok": 0, "off_should_be_on": -0.001, "off_should_be_resetting": -0.0005, "on_should_be_off": -0.0002, "on_should_be_resetting": -0.0005, "resetting_should_be_on": -0.0005, "resetting_should_be_off": -0.0002, "resetting": -0.0003, "good_should_be_patching": 0.0002, "good_should_be_compromised": 0.0005, "good_should_be_overwhelmed": 0.0005, "patching_should_be_good": -0.0005, "patching_should_be_compromised": 0.0002, "patching_should_be_overwhelmed": 0.0002, "patching": -0.0003, "compromised_should_be_good": -0.002, "compromised_should_be_patching": -0.002, "compromised_should_be_overwhelmed": -0.002, "compromised": -0.002, "overwhelmed_should_be_good": -0.002, "overwhelmed_should_be_patching": -0.002, "overwhelmed_should_be_compromised": -0.002, "overwhelmed": -0.002, "good_should_be_repairing": 0.0002, "good_should_be_restoring": 0.0002, "good_should_be_corrupt": 0.0005, "good_should_be_destroyed": 0.001, "repairing_should_be_good": -0.0005, "repairing_should_be_restoring": 0.0002, "repairing_should_be_corrupt": 0.0002, "repairing_should_be_destroyed": 0.0, "repairing": -0.0003, "restoring_should_be_good": -0.001, "restoring_should_be_repairing": -0.0002, "restoring_should_be_corrupt": 0.0001, "restoring_should_be_destroyed": 0.0002, "restoring": -0.0006, "corrupt_should_be_good": -0.001, "corrupt_should_be_repairing": -0.001, "corrupt_should_be_restoring": -0.001, "corrupt_should_be_destroyed": 0.0002, "corrupt": -0.001, "destroyed_should_be_good": -0.002, "destroyed_should_be_repairing": -0.002, "destroyed_should_be_restoring": -0.002, "destroyed_should_be_corrupt": -0.002, "destroyed": -0.002, "scanning": -0.0002, "red_ier_running": -0.0005, "green_ier_blocked": -0.001, "os_patching_duration": 5, "node_reset_duration": 5, "node_booting_duration": 3, "node_shutdown_duration": 2, "service_fixing_duration": 5, "file_system_repairing_limit": 5, "file_system_restoring_limit": 5, "file_system_scanning_limit": 5, "deterministic": true, "seed": 12345 }, "lay_down_config": [ { "item_type": "PORTS", "ports_list": [ { "port": "80" } ] }, { "item_type": "SERVICES", "service_list": [ { "name": "TCP" } ] }, { "item_type": "NODE", "node_id": "1", "name": "PC1", "node_class": "SERVICE", "node_type": "COMPUTER", "priority": "P5", "hardware_state": "ON", "ip_address": "192.168.1.2", "software_state": "GOOD", "file_system_state": "GOOD", "services": [ { "name": "TCP", "port": "80", "state": "GOOD" } ] }, { "item_type": "NODE", "node_id": "2", "name": "PC2", "node_class": "SERVICE", "node_type": "COMPUTER", "priority": "P5", "hardware_state": "ON", "ip_address": "192.168.1.3", "software_state": "GOOD", "file_system_state": "GOOD", "services": [ { "name": "TCP", "port": "80", "state": "GOOD" } ] }, { "item_type": "NODE", "node_id": "3", "name": "SWITCH1", "node_class": "ACTIVE", "node_type": "SWITCH", "priority": "P2", "hardware_state": "ON", "ip_address": "192.168.1.1", "software_state": "GOOD", "file_system_state": "GOOD" }, { "item_type": "NODE", "node_id": "4", "name": "SERVER1", "node_class": "SERVICE", "node_type": "SERVER", "priority": "P5", "hardware_state": "ON", "ip_address": "192.168.1.4", "software_state": "GOOD", "file_system_state": "GOOD", "services": [ { "name": "TCP", "port": "80", "state": "GOOD" } ] }, { "item_type": "LINK", "id": "5", "name": "link1", "bandwidth": 1000000000, "source": "1", "destination": "3" }, { "item_type": "LINK", "id": "6", "name": "link2", "bandwidth": 1000000000, "source": "2", "destination": "3" }, { "item_type": "LINK", "id": "7", "name": "link3", "bandwidth": 1000000000, "source": "3", "destination": "4" }, { "item_type": "GREEN_IER", "id": "8", "start_step": 1, "end_step": 256, "load": 10000, "protocol": "TCP", "port": "80", "source": "1", "destination": "4", "mission_criticality": 1 }, { "item_type": "GREEN_IER", "id": "9", "start_step": 1, "end_step": 256, "load": 10000, "protocol": "TCP", "port": "80", "source": "2", "destination": "4", "mission_criticality": 1 }, { "item_type": "GREEN_IER", "id": "10", "start_step": 1, "end_step": 256, "load": 10000, "protocol": "TCP", "port": "80", "source": "4", "destination": "2", "mission_criticality": 5 }, { "item_type": "ACL_RULE", "id": "11", "permission": "ALLOW", "source": "192.168.1.2", "destination": "192.168.1.4", "protocol": "TCP", "port": 80, "position": 0 }, { "item_type": "ACL_RULE", "id": "12", "permission": "ALLOW", "source": "192.168.1.3", "destination": "192.168.1.4", "protocol": "TCP", "port": 80, "position": 1 }, { "item_type": "ACL_RULE", "id": "13", "permission": "ALLOW", "source": "192.168.1.4", "destination": "192.168.1.3", "protocol": "TCP", "port": 80, "position": 2 }, { "item_type": "RED_POL", "id": "14", "start_step": 20, "end_step": 20, "targetNodeId": "1", "initiator": "DIRECT", "type": "SERVICE", "protocol": "TCP", "state": "COMPROMISED", "sourceNodeId": "NA", "sourceNodeService": "NA", "sourceNodeServiceState": "NA" }, { "item_type": "RED_IER", "id": "15", "start_step": 30, "end_step": 256, "load": 10000000, "protocol": "TCP", "port": "80", "source": "1", "destination": "4", "mission_criticality": 0 }, { "item_type": "RED_POL", "id": "16", "start_step": 40, "end_step": 40, "targetNodeId": "4", "initiator": "IER", "type": "SERVICE", "protocol": "TCP", "state": "OVERWHELMED", "sourceNodeId": "NA", "sourceNodeService": "NA", "sourceNodeServiceState": "NA" } ] } } diff --git a/tests/conftest.py b/tests/conftest.py index 9eaf1782..078a78bd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -475,7 +475,7 @@ def game_and_agent(): {"type": "NODE_SERVICE_RESTART"}, {"type": "NODE_SERVICE_DISABLE"}, {"type": "NODE_SERVICE_ENABLE"}, - {"type": "NODE_SERVICE_PATCH"}, + {"type": "NODE_SERVICE_FIX"}, {"type": "NODE_APPLICATION_EXECUTE"}, {"type": "NODE_APPLICATION_SCAN"}, {"type": "NODE_APPLICATION_CLOSE"}, diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index 98e6ea5d..5aa93e27 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -65,9 +65,9 @@ def test_node_service_scan_integration(game_and_agent: Tuple[PrimaiteGame, Proxy assert svc.health_state_visible == SoftwareHealthState.COMPROMISED -def test_node_service_patch_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): +def test_node_service_fix_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): """ - Test that the NodeServicePatchAction can form a request and that it is accepted by the simulation. + Test that the NodeServiceFixAction can form a request and that it is accepted by the simulation. When you initiate a patch action, the software health state turns to PATCHING, then after a few steps, it goes to GOOD. @@ -79,7 +79,7 @@ def test_node_service_patch_integration(game_and_agent: Tuple[PrimaiteGame, Prox svc.health_state_actual = SoftwareHealthState.COMPROMISED # 2: Apply a patch action - action = ("NODE_SERVICE_PATCH", {"node_id": 1, "service_id": 0}) + action = ("NODE_SERVICE_FIX", {"node_id": 1, "service_id": 0}) agent.store_action(action) game.step() diff --git a/tests/integration_tests/test_simulation/test_request_response.py b/tests/integration_tests/test_simulation/test_request_response.py index aee5c816..5df04fbb 100644 --- a/tests/integration_tests/test_simulation/test_request_response.py +++ b/tests/integration_tests/test_simulation/test_request_response.py @@ -23,7 +23,7 @@ def test_successful_application_requests(example_network): resp_1 = net.apply_request(["node", "client_1", "application", "TestApplication", "scan"]) assert resp_1 == RequestResponse(status="success", data={}) - resp_2 = net.apply_request(["node", "client_1", "application", "TestApplication", "patch"]) + resp_2 = net.apply_request(["node", "client_1", "application", "TestApplication", "fix"]) assert resp_2 == RequestResponse(status="success", data={}) resp_3 = net.apply_request(["node", "client_1", "application", "TestApplication", "compromise"]) assert resp_3 == RequestResponse(status="success", data={}) @@ -46,7 +46,7 @@ def test_successful_service_requests(example_network): "resume", "compromise", "scan", - "patch", + "fix", ]: resp_1 = net.apply_request(["node", "server_1", "service", "TestService", verb]) assert resp_1 == RequestResponse(status="success", data={}) diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py index 6247a100..fbdd9bef 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py @@ -46,5 +46,5 @@ def test_application_describe_states(application): application.set_health_state(SoftwareHealthState.COMPROMISED) assert SoftwareHealthState.COMPROMISED.value == application.describe_state().get("health_state_actual") - application.patch() + application.fix() assert SoftwareHealthState.PATCHING.value == application.describe_state().get("health_state_actual") diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py index 5f10ec96..b9b43b25 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py @@ -99,8 +99,6 @@ def test_query_when_client_is_closed(database_client_on_computer): assert database_client.query(sql="test") is False - -def test_query_fail_to_connect(database_client_on_computer): """Database client query should return False if the connect attempt fails.""" database_client, computer = database_client_on_computer diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py index 714644e4..dd2d7752 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py @@ -80,12 +80,12 @@ def test_service_enable(service): assert service.operating_state == ServiceOperatingState.STOPPED -def test_service_patch(service): - """Test that a service can be patched and that it takes two timesteps to complete.""" +def test_service_fix(service): + """Test that a service can be fixed and that it takes two timesteps to complete.""" service.start() assert service.health_state_actual == SoftwareHealthState.GOOD - service.apply_request(["patch"]) + service.apply_request(["fix"]) assert service.health_state_actual == SoftwareHealthState.PATCHING service.apply_timestep(1) assert service.health_state_actual == SoftwareHealthState.PATCHING diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py index ac36c660..59d9499b 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py @@ -136,16 +136,16 @@ def test_compromised_service_remains_compromised(service): assert service.health_state_actual == SoftwareHealthState.COMPROMISED -def test_service_patching(service): +def test_service_fixing(service): service.start() assert service.health_state_actual == SoftwareHealthState.GOOD service.set_health_state(SoftwareHealthState.COMPROMISED) - service.patch() + service.fix() assert service.health_state_actual == SoftwareHealthState.PATCHING - for i in range(service.patching_duration + 1): + for i in range(service.fixing_duration + 1): service.apply_timestep(i) assert service.health_state_actual == SoftwareHealthState.GOOD From 128e2227d6068dd642c66893638d48b33c2d6b0d Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Tue, 26 Mar 2024 12:39:11 +0000 Subject: [PATCH 3/7] #2404 add missing test and revert some name changes --- benchmark/config/benchmark_training_config.yaml | 2 +- .../results/v2.0.0/v2.0.0_benchmark_metadata.json | 2 +- .../simulator/system/applications/application.py | 2 +- .../_system/_applications/test_database_client.py | 13 +++++++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/benchmark/config/benchmark_training_config.yaml b/benchmark/config/benchmark_training_config.yaml index 17811586..02b6377c 100644 --- a/benchmark/config/benchmark_training_config.yaml +++ b/benchmark/config/benchmark_training_config.yaml @@ -158,7 +158,7 @@ green_ier_blocked: -0.001 # Patching / Reset durations os_patching_duration: 5 # The time taken to patch the OS node_reset_duration: 5 # The time taken to reset a node (hardware) -service_fixing_duration: 5 # The time taken to patch a service +service_patching_duration: 5 # The time taken to patch a service file_system_repairing_limit: 5 # The time take to repair the file system file_system_restoring_limit: 5 # The time take to restore the file system file_system_scanning_limit: 5 # The time taken to scan the file system diff --git a/benchmark/results/v2.0.0/v2.0.0_benchmark_metadata.json b/benchmark/results/v2.0.0/v2.0.0_benchmark_metadata.json index 3ed57745..b2745967 100644 --- a/benchmark/results/v2.0.0/v2.0.0_benchmark_metadata.json +++ b/benchmark/results/v2.0.0/v2.0.0_benchmark_metadata.json @@ -5634,7 +5634,7 @@ "green_ier_blocked": -0.001, "os_patching_duration": 5, "node_reset_duration": 5, - "service_fixing_duration": 5, + "service_patching_duration": 5, "file_system_repairing_limit": 5, "file_system_restoring_limit": 5, "file_system_scanning_limit": 5 diff --git a/src/primaite/simulator/system/applications/application.py b/src/primaite/simulator/system/applications/application.py index 4ea893e0..617fdc23 100644 --- a/src/primaite/simulator/system/applications/application.py +++ b/src/primaite/simulator/system/applications/application.py @@ -117,7 +117,7 @@ class Application(IOSoftware): """The main application loop.""" pass - def close(self) -> None: + def close(self) -> bool: """Close the Application.""" if self.operating_state == ApplicationOperatingState.RUNNING: self.sys_log.info(f"Closed Application{self.name}") diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py index b9b43b25..5098fcde 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py @@ -111,6 +111,19 @@ def test_query_when_client_is_closed(database_client_on_computer): assert database_client.query(sql="test") is False +def test_query_fail_to_connect(database_client_on_computer): + """Database client query should return False if the connect attempt fails.""" + database_client, computer = database_client_on_computer + + def return_false(): + return False + + database_client.connect = return_false + database_client.connected = False + + assert database_client.query(sql="test") is False + + def test_client_receives_response_when_closed(database_client_on_computer): """Database client receive should return False when it is closed.""" database_client, computer = database_client_on_computer From 53126e79dfe793547be0f34826a2f1e88cb7d7ab Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Tue, 26 Mar 2024 12:43:45 +0000 Subject: [PATCH 4/7] #2404 remove extra code in test_query_when_client_is_closed --- .../_system/_applications/test_database_client.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py index 5098fcde..5f10ec96 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py @@ -99,17 +99,6 @@ def test_query_when_client_is_closed(database_client_on_computer): assert database_client.query(sql="test") is False - """Database client query should return False if the connect attempt fails.""" - database_client, computer = database_client_on_computer - - def return_false(): - return False - - database_client.connect = return_false - database_client.connected = False - - assert database_client.query(sql="test") is False - def test_query_fail_to_connect(database_client_on_computer): """Database client query should return False if the connect attempt fails.""" From b3c1b6b7a5e40ac1a06d9b896d89fadd8f39604c Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Tue, 26 Mar 2024 12:52:16 +0000 Subject: [PATCH 5/7] #2404 quick fix for failing test_query_fail_to_connect --- .../_simulator/_system/_applications/test_database_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py index 5f10ec96..4d964fa1 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_database_client.py @@ -104,7 +104,7 @@ def test_query_fail_to_connect(database_client_on_computer): """Database client query should return False if the connect attempt fails.""" database_client, computer = database_client_on_computer - def return_false(): + def return_false(**kwargs): return False database_client.connect = return_false From 5860c74ef97c7378a78ad2f1e2f3a13a99b0bccf Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Tue, 26 Mar 2024 16:41:11 +0000 Subject: [PATCH 6/7] #2404 change software state from patching to fixing to align with CAOS v0.8 --- docs/source/about.rst | 22 +++++++++---------- .../Data-Manipulation-E2E-Demonstration.ipynb | 2 +- .../services/database/database_service.py | 2 +- src/primaite/simulator/system/software.py | 8 +++---- .../game_layer/test_actions.py | 12 +++++----- .../_applications/test_applications.py | 2 +- .../_system/_services/test_service_actions.py | 4 ++-- .../_system/_services/test_services.py | 4 ++-- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/source/about.rst b/docs/source/about.rst index 3f905933..782103d6 100644 --- a/docs/source/about.rst +++ b/docs/source/about.rst @@ -41,11 +41,11 @@ The game layer is built on top of the simulator and it consumes the simulation a * Hardware State (ON, OFF, RESETTING, SHUTTING_DOWN, BOOTING - enumeration) Active Nodes also have the following attributes (Class: Active Node): * IP Address - * Software State (GOOD, PATCHING, COMPROMISED - enumeration) + * Software State (GOOD, FIXING, COMPROMISED - enumeration) * File System State (GOOD, CORRUPT, DESTROYED, REPAIRING, RESTORING - enumeration) Service Nodes also have the following attributes (Class: Service Node): * List of Services (where service is composed of service name and port). There is no theoretical limit on the number of services that can be modelled. Services and protocols are currently intrinsically linked (i.e. a service is an application on a node transmitting traffic of this protocol type) - * Service state (GOOD, PATCHING, COMPROMISED, OVERWHELMED - enumeration) + * Service state (GOOD, FIXING, COMPROMISED, OVERWHELMED - enumeration) Passive Nodes are currently not used (but may be employed for non IP-based components such as machinery actuators in future releases). **Links** Links are modelled both as network edges (networkx) and as Python classes, in order to extend their functionality. Links include the following attributes: @@ -70,8 +70,8 @@ The game layer is built on top of the simulator and it consumes the simulation a * Running status (i.e. on / off) The application of green agent IERs between a source and destination follows a number of rules. Specifically: 1. Does the current simulation time step fall between IER start and end step - 2. Is the source node operational (both physically and at an O/S level), and is the service (protocol / port) associated with the IER (a) present on this node, and (b) in an operational state (i.e. not PATCHING) - 3. Is the destination node operational (both physically and at an O/S level), and is the service (protocol / port) associated with the IER (a) present on this node, and (b) in an operational state (i.e. not PATCHING) + 2. Is the source node operational (both physically and at an O/S level), and is the service (protocol / port) associated with the IER (a) present on this node, and (b) in an operational state (i.e. not FIXING) + 3. Is the destination node operational (both physically and at an O/S level), and is the service (protocol / port) associated with the IER (a) present on this node, and (b) in an operational state (i.e. not FIXING) 4. Are there any Access Control List rules in place that prevent the application of this IER 5. Are all switches in the (OSPF) path between source and destination operational (both physically and at an O/S level) For red agent IERs, the application of IERs between a source and destination follows a number of subtly different rules. Specifically: @@ -95,7 +95,7 @@ The game layer is built on top of the simulator and it consumes the simulation a * Active Nodes and Service Nodes: * Software State: * GOOD - * PATCHING - when a status of patching is entered, the node will automatically exit this state after a number of steps (as defined by the osPatchingDuration configuration item) after which it returns to a GOOD state + * FIXING - when a status of FIXING is entered, the node will automatically exit this state after a number of steps (as defined by the osFIXINGDuration configuration item) after which it returns to a GOOD state * COMPROMISED * File System State: * GOOD @@ -106,7 +106,7 @@ The game layer is built on top of the simulator and it consumes the simulation a * Service Nodes only: * Service State (for any associated service): * GOOD - * PATCHING - when a status of patching is entered, the service will automatically exit this state after a number of steps (as defined by the servicePatchingDuration configuration item) after which it returns to a GOOD state + * FIXING - when a status of FIXING is entered, the service will automatically exit this state after a number of steps (as defined by the serviceFIXINGDuration configuration item) after which it returns to a GOOD state * COMPROMISED * OVERWHELMED Red agent pattern-of-life has an additional feature not found in the green pattern-of-life. This is the ability to influence the state of the attributes of a node via a number of different conditions: @@ -211,8 +211,8 @@ The game layer is built on top of the simulator and it consumes the simulation a Hardware State (1=ON, 2=OFF, 3=RESETTING, 4=SHUTTING_DOWN, 5=BOOTING) Operating System State (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED) File System State (0=none, 1=GOOD, 2=CORRUPT, 3=DESTROYED, 4=REPAIRING, 5=RESTORING) - Service1/Protocol1 state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED) - Service2/Protocol2 state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED) + Service1/Protocol1 state (0=none, 1=GOOD, 2=FIXING, 3=COMPROMISED) + Service2/Protocol2 state (0=none, 1=GOOD, 2=FIXING, 3=COMPROMISED) ] (Note that each service available in the network is provided as a column, although not all nodes may utilise all services) For the links, the following statuses are represented: @@ -241,8 +241,8 @@ The game layer is built on top of the simulator and it consumes the simulation a hardware_state (0=none, 1=ON, 2=OFF, 3=RESETTING, 4=SHUTTING_DOWN, 5=BOOTING) software_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED) file_system_state (0=none, 1=GOOD, 2=CORRUPT, 3=DESTROYED, 4=REPAIRING, 5=RESTORING) - service1_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED) - service2_state (0=none, 1=GOOD, 2=PATCHING, 3=COMPROMISED) + service1_state (0=none, 1=GOOD, 2=FIXING, 3=COMPROMISED) + service2_state (0=none, 1=GOOD, 2=FIXING, 3=COMPROMISED) ] In a network with three nodes and two services, the full observation space would have 15 elements. It can be written with ``gym`` notation to indicate the number of discrete options for each of the elements of the observation space. For example: .. code-block:: @@ -278,7 +278,7 @@ The game layer is built on top of the simulator and it consumes the simulation a 3. Any (Agent can take both node-based and ACL-based actions) The choice of action space used during a training session is determined in the config_[name].yaml file. **Node-Based** - The agent is able to influence the status of nodes by switching them off, resetting, or patching operating systems and services. In this instance, the action space is a Gymnasium spaces.Discrete type, as follows: + The agent is able to influence the status of nodes by switching them off, resetting, or FIXING operating systems and services. In this instance, the action space is a Gymnasium spaces.Discrete type, as follows: * Dictionary item {... ,1: [x1, x2, x3,x4] ...} The placeholders inside the list under the key '1' mean the following: * [0, num nodes] - Node ID (0 = nothing, node ID) diff --git a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb index 7ec58b2c..ebe0f329 100644 --- a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb @@ -520,7 +520,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The patching takes two steps, so the reward hasn't changed yet. Let's do nothing for another timestep, the reward should improve.\n", + "The fixing takes two steps, so the reward hasn't changed yet. Let's do nothing for another timestep, the reward should improve.\n", "\n", "The reward will increase slightly as soon as the file finishes restoring. Then, the reward will increase to 1 when both green agents make successful requests.\n", "\n", diff --git a/src/primaite/simulator/system/services/database/database_service.py b/src/primaite/simulator/system/services/database/database_service.py index 411359a2..541a15c2 100644 --- a/src/primaite/simulator/system/services/database/database_service.py +++ b/src/primaite/simulator/system/services/database/database_service.py @@ -305,7 +305,7 @@ class DatabaseService(Service): return super().apply_timestep(timestep) def _update_fix_status(self) -> None: - """Perform a database restore when the patching countdown is finished.""" + """Perform a database restore when the FIXING countdown is finished.""" super()._update_fix_status() if self._fixing_countdown is None: self.restore_backup() diff --git a/src/primaite/simulator/system/software.py b/src/primaite/simulator/system/software.py index 9b54f802..ab60adde 100644 --- a/src/primaite/simulator/system/software.py +++ b/src/primaite/simulator/system/software.py @@ -43,8 +43,8 @@ class SoftwareHealthState(Enum): "Unused state." GOOD = 1 "The software is in a good and healthy condition." - PATCHING = 2 - "The software is undergoing patching or updates." + FIXING = 2 + "The software is undergoing FIXING or updates." COMPROMISED = 3 "The software's security has been compromised." OVERWHELMED = 4 @@ -198,7 +198,7 @@ class Software(SimComponent): """Perform a fix on the software.""" if self.health_state_actual in (SoftwareHealthState.COMPROMISED, SoftwareHealthState.GOOD): self._fixing_countdown = self.fixing_duration - self.set_health_state(SoftwareHealthState.PATCHING) + self.set_health_state(SoftwareHealthState.FIXING) return True return False @@ -221,7 +221,7 @@ class Software(SimComponent): :param timestep: The current timestep of the simulation. """ super().apply_timestep(timestep) - if self.health_state_actual == SoftwareHealthState.PATCHING: + if self.health_state_actual == SoftwareHealthState.FIXING: self._update_fix_status() diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index 5aa93e27..b3a52cd8 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -69,7 +69,7 @@ def test_node_service_fix_integration(game_and_agent: Tuple[PrimaiteGame, ProxyA """ Test that the NodeServiceFixAction can form a request and that it is accepted by the simulation. - When you initiate a patch action, the software health state turns to PATCHING, then after a few steps, it goes + When you initiate a patch action, the software health state turns to FIXING, then after a few steps, it goes to GOOD. """ game, agent = game_and_agent @@ -83,8 +83,8 @@ def test_node_service_fix_integration(game_and_agent: Tuple[PrimaiteGame, ProxyA agent.store_action(action) game.step() - # 3: Check that the service is now in the patching state - assert svc.health_state_actual == SoftwareHealthState.PATCHING + # 3: Check that the service is now in the FIXING state + assert svc.health_state_actual == SoftwareHealthState.FIXING # 4: perform a few do-nothing steps and check that the service is now in the good state action = ("DONOTHING", {}) @@ -413,7 +413,7 @@ def test_node_application_scan_integration(game_and_agent: Tuple[PrimaiteGame, P def test_node_application_fix_integration(game_and_agent: Tuple[PrimaiteGame, ProxyAgent]): """Test that the NodeApplicationFixAction can form a request and that it is accepted by the simulation. - When you initiate a fix action, the software health state turns to PATCHING, then after a few steps, it goes + When you initiate a fix action, the software health state turns to FIXING, then after a few steps, it goes to GOOD.""" game, agent = game_and_agent @@ -428,8 +428,8 @@ def test_node_application_fix_integration(game_and_agent: Tuple[PrimaiteGame, Pr agent.store_action(action) game.step() - # 3: Check that the application is now in the patching state - assert browser.health_state_actual == SoftwareHealthState.PATCHING + # 3: Check that the application is now in the FIXING state + assert browser.health_state_actual == SoftwareHealthState.FIXING # 4: perform a few do-nothing steps and check that the application is now in the good state action = ("DONOTHING", {}) diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py index fbdd9bef..90c3f303 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py @@ -47,4 +47,4 @@ def test_application_describe_states(application): assert SoftwareHealthState.COMPROMISED.value == application.describe_state().get("health_state_actual") application.fix() - assert SoftwareHealthState.PATCHING.value == application.describe_state().get("health_state_actual") + assert SoftwareHealthState.FIXING.value == application.describe_state().get("health_state_actual") diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py index dd2d7752..edc111e3 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_service_actions.py @@ -86,8 +86,8 @@ def test_service_fix(service): assert service.health_state_actual == SoftwareHealthState.GOOD service.apply_request(["fix"]) - assert service.health_state_actual == SoftwareHealthState.PATCHING + assert service.health_state_actual == SoftwareHealthState.FIXING service.apply_timestep(1) - assert service.health_state_actual == SoftwareHealthState.PATCHING + assert service.health_state_actual == SoftwareHealthState.FIXING service.apply_timestep(2) assert service.health_state_actual == SoftwareHealthState.GOOD diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py index 59d9499b..4deeef74 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_services.py @@ -93,7 +93,7 @@ def test_restart_compromised(service): """ Service should be compromised even after reset. - Only way to remove compromised status is via patching. + Only way to remove compromised status is via FIXING. """ timestep = 0 @@ -143,7 +143,7 @@ def test_service_fixing(service): service.set_health_state(SoftwareHealthState.COMPROMISED) service.fix() - assert service.health_state_actual == SoftwareHealthState.PATCHING + assert service.health_state_actual == SoftwareHealthState.FIXING for i in range(service.fixing_duration + 1): service.apply_timestep(i) From 94e637375d73c830435bdebfcc63dabe46a16143 Mon Sep 17 00:00:00 2001 From: Cristian-VM2 Date: Tue, 26 Mar 2024 17:02:51 +0000 Subject: [PATCH 7/7] #2404 update software state to fixing in UC2 e2e notebook [skip ci] --- .../notebooks/Data-Manipulation-E2E-Demonstration.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb index ebe0f329..60d40f9c 100644 --- a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb @@ -208,7 +208,7 @@ "|--|--|\n", "|0|UNUSED|\n", "|1|GOOD|\n", - "|2|PATCHING|\n", + "|2|FIXING|\n", "|3|COMPROMISED|\n", "|4|OVERWHELMED|\n", "\n",