Merge remote-tracking branch 'origin/dev' into feature/2137-refactor-request-api
This commit is contained in:
@@ -19,7 +19,7 @@ game:
|
||||
- UDP
|
||||
|
||||
agents:
|
||||
- ref: client_1_green_user
|
||||
- ref: client_2_green_user
|
||||
team: GREEN
|
||||
type: GreenWebBrowsingAgent
|
||||
observation_space:
|
||||
@@ -489,6 +489,23 @@ agents:
|
||||
max_services_per_node: 2
|
||||
max_nics_per_node: 8
|
||||
max_acl_rules: 10
|
||||
ip_address_order:
|
||||
- node_ref: domain_controller
|
||||
nic_num: 1
|
||||
- node_ref: web_server
|
||||
nic_num: 1
|
||||
- node_ref: database_server
|
||||
nic_num: 1
|
||||
- node_ref: backup_server
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 1
|
||||
- node_ref: client_1
|
||||
nic_num: 1
|
||||
- node_ref: client_2
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 2
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
|
||||
@@ -23,7 +23,7 @@ game:
|
||||
- UDP
|
||||
|
||||
agents:
|
||||
- ref: client_1_green_user
|
||||
- ref: client_2_green_user
|
||||
team: GREEN
|
||||
type: GreenWebBrowsingAgent
|
||||
observation_space:
|
||||
@@ -493,6 +493,23 @@ agents:
|
||||
max_services_per_node: 2
|
||||
max_nics_per_node: 8
|
||||
max_acl_rules: 10
|
||||
ip_address_order:
|
||||
- node_ref: domain_controller
|
||||
nic_num: 1
|
||||
- node_ref: web_server
|
||||
nic_num: 1
|
||||
- node_ref: database_server
|
||||
nic_num: 1
|
||||
- node_ref: backup_server
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 1
|
||||
- node_ref: client_1
|
||||
nic_num: 1
|
||||
- node_ref: client_2
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 2
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
|
||||
@@ -29,7 +29,7 @@ game:
|
||||
- UDP
|
||||
|
||||
agents:
|
||||
- ref: client_1_green_user
|
||||
- ref: client_2_green_user
|
||||
team: GREEN
|
||||
type: GreenWebBrowsingAgent
|
||||
observation_space:
|
||||
@@ -500,6 +500,23 @@ agents:
|
||||
max_services_per_node: 2
|
||||
max_nics_per_node: 8
|
||||
max_acl_rules: 10
|
||||
ip_address_order:
|
||||
- node_ref: domain_controller
|
||||
nic_num: 1
|
||||
- node_ref: web_server
|
||||
nic_num: 1
|
||||
- node_ref: database_server
|
||||
nic_num: 1
|
||||
- node_ref: backup_server
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 1
|
||||
- node_ref: client_1
|
||||
nic_num: 1
|
||||
- node_ref: client_2
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 2
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
@@ -929,6 +946,23 @@ agents:
|
||||
max_services_per_node: 2
|
||||
max_nics_per_node: 8
|
||||
max_acl_rules: 10
|
||||
ip_address_order:
|
||||
- node_ref: domain_controller
|
||||
nic_num: 1
|
||||
- node_ref: web_server
|
||||
nic_num: 1
|
||||
- node_ref: database_server
|
||||
nic_num: 1
|
||||
- node_ref: backup_server
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 1
|
||||
- node_ref: client_1
|
||||
nic_num: 1
|
||||
- node_ref: client_2
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 2
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
|
||||
@@ -27,7 +27,7 @@ game:
|
||||
- UDP
|
||||
|
||||
agents:
|
||||
- ref: client_1_green_user
|
||||
- ref: client_2_green_user
|
||||
team: GREEN
|
||||
type: GreenWebBrowsingAgent
|
||||
observation_space:
|
||||
@@ -500,6 +500,23 @@ agents:
|
||||
max_services_per_node: 2
|
||||
max_nics_per_node: 8
|
||||
max_acl_rules: 10
|
||||
ip_address_order:
|
||||
- node_ref: domain_controller
|
||||
nic_num: 1
|
||||
- node_ref: web_server
|
||||
nic_num: 1
|
||||
- node_ref: database_server
|
||||
nic_num: 1
|
||||
- node_ref: backup_server
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 1
|
||||
- node_ref: client_1
|
||||
nic_num: 1
|
||||
- node_ref: client_2
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 2
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
|
||||
@@ -23,7 +23,7 @@ game:
|
||||
- UDP
|
||||
|
||||
agents:
|
||||
- ref: client_1_green_user
|
||||
- ref: client_2_green_user
|
||||
team: GREEN
|
||||
type: GreenWebBrowsingAgent
|
||||
observation_space:
|
||||
@@ -501,6 +501,23 @@ agents:
|
||||
max_services_per_node: 2
|
||||
max_nics_per_node: 8
|
||||
max_acl_rules: 10
|
||||
ip_address_order:
|
||||
- node_ref: domain_controller
|
||||
nic_num: 1
|
||||
- node_ref: web_server
|
||||
nic_num: 1
|
||||
- node_ref: database_server
|
||||
nic_num: 1
|
||||
- node_ref: backup_server
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 1
|
||||
- node_ref: client_1
|
||||
nic_num: 1
|
||||
- node_ref: client_2
|
||||
nic_num: 1
|
||||
- node_ref: security_suite
|
||||
nic_num: 2
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
|
||||
@@ -40,6 +40,9 @@ from primaite.simulator.network.hardware.base import Link, Node
|
||||
class TestService(Service):
|
||||
"""Test Service class"""
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
return super().describe_state()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs["name"] = "TestService"
|
||||
kwargs["port"] = Port.HTTP
|
||||
@@ -60,7 +63,7 @@ class TestApplication(Application):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
pass
|
||||
return super().describe_state()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@@ -167,7 +170,7 @@ def example_network() -> Network:
|
||||
-------------- --------------
|
||||
| client_1 |----- ----| server_1 |
|
||||
-------------- | -------------- -------------- -------------- | --------------
|
||||
------| switch_1 |------| router_1 |------| switch_2 |------
|
||||
------| switch_2 |------| router_1 |------| switch_1 |------
|
||||
-------------- | -------------- -------------- -------------- | --------------
|
||||
| client_2 |---- ----| server_2 |
|
||||
-------------- --------------
|
||||
|
||||
@@ -22,7 +22,7 @@ def test_data_manipulation(uc2_network):
|
||||
assert db_client.query("SELECT")
|
||||
|
||||
# Now we run the DataManipulationBot
|
||||
db_manipulation_bot.run()
|
||||
db_manipulation_bot.attack()
|
||||
|
||||
# Now check that the DB client on the web_server cannot query the users table on the database
|
||||
assert not db_client.query("SELECT")
|
||||
|
||||
180
tests/integration_tests/network/test_broadcast.py
Normal file
180
tests/integration_tests/network/test_broadcast.py
Normal file
@@ -0,0 +1,180 @@
|
||||
from ipaddress import IPv4Address, IPv4Network
|
||||
from typing import Any, Dict, List, Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.simulator.network.container import Network
|
||||
from primaite.simulator.network.hardware.nodes.computer import Computer
|
||||
from primaite.simulator.network.hardware.nodes.server import Server
|
||||
from primaite.simulator.network.hardware.nodes.switch import Switch
|
||||
from primaite.simulator.network.transmission.network_layer import IPProtocol
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.applications.application import Application
|
||||
from primaite.simulator.system.services.service import Service
|
||||
|
||||
|
||||
class BroadcastService(Service):
|
||||
"""A service for sending broadcast and unicast messages over a network."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# Set default service properties for broadcasting
|
||||
kwargs["name"] = "BroadcastService"
|
||||
kwargs["port"] = Port.HTTP
|
||||
kwargs["protocol"] = IPProtocol.TCP
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
# Implement state description for the service
|
||||
pass
|
||||
|
||||
def unicast(self, ip_address: IPv4Address):
|
||||
# Send a unicast payload to a specific IP address
|
||||
super().send(
|
||||
payload="unicast",
|
||||
dest_ip_address=ip_address,
|
||||
dest_port=Port.HTTP,
|
||||
)
|
||||
|
||||
def broadcast(self, ip_network: IPv4Network):
|
||||
# Send a broadcast payload to an entire IP network
|
||||
super().send(
|
||||
payload="broadcast",
|
||||
dest_ip_address=ip_network,
|
||||
dest_port=Port.HTTP,
|
||||
)
|
||||
|
||||
|
||||
class BroadcastClient(Application):
|
||||
"""A client application to receive broadcast and unicast messages."""
|
||||
|
||||
payloads_received: List = []
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# Set default client properties
|
||||
kwargs["name"] = "BroadcastClient"
|
||||
kwargs["port"] = Port.HTTP
|
||||
kwargs["protocol"] = IPProtocol.TCP
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
# Implement state description for the application
|
||||
pass
|
||||
|
||||
def receive(self, payload: Any, session_id: str, **kwargs) -> bool:
|
||||
# Append received payloads to the list and print a message
|
||||
self.payloads_received.append(payload)
|
||||
print(f"Payload: {payload} received on node {self.sys_log.hostname}")
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def broadcast_network() -> Network:
|
||||
network = Network()
|
||||
|
||||
client_1 = Computer(
|
||||
hostname="client_1",
|
||||
ip_address="192.168.1.2",
|
||||
subnet_mask="255.255.255.0",
|
||||
default_gateway="192.168.1.1",
|
||||
start_up_duration=0,
|
||||
)
|
||||
client_1.power_on()
|
||||
client_1.software_manager.install(BroadcastClient)
|
||||
application_1 = client_1.software_manager.software["BroadcastClient"]
|
||||
application_1.run()
|
||||
|
||||
client_2 = Computer(
|
||||
hostname="client_2",
|
||||
ip_address="192.168.1.3",
|
||||
subnet_mask="255.255.255.0",
|
||||
default_gateway="192.168.1.1",
|
||||
start_up_duration=0,
|
||||
)
|
||||
client_2.power_on()
|
||||
client_2.software_manager.install(BroadcastClient)
|
||||
application_2 = client_2.software_manager.software["BroadcastClient"]
|
||||
application_2.run()
|
||||
|
||||
server_1 = Server(
|
||||
hostname="server_1",
|
||||
ip_address="192.168.1.1",
|
||||
subnet_mask="255.255.255.0",
|
||||
default_gateway="192.168.1.1",
|
||||
start_up_duration=0,
|
||||
)
|
||||
server_1.power_on()
|
||||
|
||||
server_1.software_manager.install(BroadcastService)
|
||||
service: BroadcastService = server_1.software_manager.software["BroadcastService"]
|
||||
service.start()
|
||||
|
||||
switch_1 = Switch(hostname="switch_1", num_ports=6, start_up_duration=0)
|
||||
switch_1.power_on()
|
||||
|
||||
network.connect(endpoint_a=client_1.ethernet_port[1], endpoint_b=switch_1.switch_ports[1])
|
||||
network.connect(endpoint_a=client_2.ethernet_port[1], endpoint_b=switch_1.switch_ports[2])
|
||||
network.connect(endpoint_a=server_1.ethernet_port[1], endpoint_b=switch_1.switch_ports[3])
|
||||
|
||||
return network
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def broadcast_service_and_clients(broadcast_network) -> Tuple[BroadcastService, BroadcastClient, BroadcastClient]:
|
||||
client_1: BroadcastClient = broadcast_network.get_node_by_hostname("client_1").software_manager.software[
|
||||
"BroadcastClient"
|
||||
]
|
||||
client_2: BroadcastClient = broadcast_network.get_node_by_hostname("client_2").software_manager.software[
|
||||
"BroadcastClient"
|
||||
]
|
||||
service: BroadcastService = broadcast_network.get_node_by_hostname("server_1").software_manager.software[
|
||||
"BroadcastService"
|
||||
]
|
||||
|
||||
return service, client_1, client_2
|
||||
|
||||
|
||||
def test_broadcast_correct_subnet(broadcast_service_and_clients):
|
||||
service, client_1, client_2 = broadcast_service_and_clients
|
||||
|
||||
assert not client_1.payloads_received
|
||||
assert not client_2.payloads_received
|
||||
|
||||
service.broadcast(IPv4Network("192.168.1.0/24"))
|
||||
|
||||
assert client_1.payloads_received == ["broadcast"]
|
||||
assert client_2.payloads_received == ["broadcast"]
|
||||
|
||||
|
||||
def test_broadcast_incorrect_subnet(broadcast_service_and_clients):
|
||||
service, client_1, client_2 = broadcast_service_and_clients
|
||||
|
||||
assert not client_1.payloads_received
|
||||
assert not client_2.payloads_received
|
||||
|
||||
service.broadcast(IPv4Network("192.168.2.0/24"))
|
||||
|
||||
assert not client_1.payloads_received
|
||||
assert not client_2.payloads_received
|
||||
|
||||
|
||||
def test_unicast_correct_address(broadcast_service_and_clients):
|
||||
service, client_1, client_2 = broadcast_service_and_clients
|
||||
|
||||
assert not client_1.payloads_received
|
||||
assert not client_2.payloads_received
|
||||
|
||||
service.unicast(IPv4Address("192.168.1.2"))
|
||||
|
||||
assert client_1.payloads_received == ["unicast"]
|
||||
assert not client_2.payloads_received
|
||||
|
||||
|
||||
def test_unicast_incorrect_address(broadcast_service_and_clients):
|
||||
service, client_1, client_2 = broadcast_service_and_clients
|
||||
|
||||
assert not client_1.payloads_received
|
||||
assert not client_2.payloads_received
|
||||
|
||||
service.unicast(IPv4Address("192.168.2.2"))
|
||||
|
||||
assert not client_1.payloads_received
|
||||
assert not client_2.payloads_received
|
||||
@@ -1,11 +1,16 @@
|
||||
from ipaddress import IPv4Address
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.simulator.network.container import Network
|
||||
from primaite.simulator.network.hardware.base import Link, NIC, Node, NodeOperatingState
|
||||
from primaite.simulator.network.hardware.nodes.computer import Computer
|
||||
from primaite.simulator.network.hardware.nodes.router import ACLAction, Router
|
||||
from primaite.simulator.network.transmission.network_layer import IPProtocol
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.services.ntp.ntp_client import NTPClient
|
||||
from primaite.simulator.system.services.ntp.ntp_server import NTPServer
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@@ -34,6 +39,69 @@ def pc_a_pc_b_router_1() -> Tuple[Node, Node, Router]:
|
||||
return pc_a, pc_b, router_1
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def multi_hop_network() -> Network:
|
||||
network = Network()
|
||||
|
||||
# Configure PC A
|
||||
pc_a = Computer(
|
||||
hostname="pc_a",
|
||||
ip_address="192.168.0.2",
|
||||
subnet_mask="255.255.255.0",
|
||||
default_gateway="192.168.0.1",
|
||||
start_up_duration=0,
|
||||
)
|
||||
pc_a.power_on()
|
||||
network.add_node(pc_a)
|
||||
|
||||
# Configure Router 1
|
||||
router_1 = Router(hostname="router_1", start_up_duration=0)
|
||||
router_1.power_on()
|
||||
network.add_node(router_1)
|
||||
|
||||
# Configure the connection between PC A and Router 1 port 2
|
||||
router_1.configure_port(2, "192.168.0.1", "255.255.255.0")
|
||||
network.connect(pc_a.ethernet_port[1], router_1.ethernet_ports[2])
|
||||
router_1.enable_port(2)
|
||||
|
||||
# Configure Router 1 ACLs
|
||||
router_1.acl.add_rule(action=ACLAction.PERMIT, src_port=Port.ARP, dst_port=Port.ARP, position=22)
|
||||
router_1.acl.add_rule(action=ACLAction.PERMIT, protocol=IPProtocol.ICMP, position=23)
|
||||
|
||||
# Configure PC B
|
||||
pc_b = Computer(
|
||||
hostname="pc_b",
|
||||
ip_address="192.168.2.2",
|
||||
subnet_mask="255.255.255.0",
|
||||
default_gateway="192.168.2.1",
|
||||
start_up_duration=0,
|
||||
)
|
||||
pc_b.power_on()
|
||||
network.add_node(pc_b)
|
||||
|
||||
# Configure Router 2
|
||||
router_2 = Router(hostname="router_2", start_up_duration=0)
|
||||
router_2.power_on()
|
||||
network.add_node(router_2)
|
||||
|
||||
# Configure the connection between PC B and Router 2 port 2
|
||||
router_2.configure_port(2, "192.168.2.1", "255.255.255.0")
|
||||
network.connect(pc_b.ethernet_port[1], router_2.ethernet_ports[2])
|
||||
router_2.enable_port(2)
|
||||
|
||||
# Configure Router 2 ACLs
|
||||
router_2.acl.add_rule(action=ACLAction.PERMIT, src_port=Port.ARP, dst_port=Port.ARP, position=22)
|
||||
router_2.acl.add_rule(action=ACLAction.PERMIT, protocol=IPProtocol.ICMP, position=23)
|
||||
|
||||
# Configure the connection between Router 1 port 1 and Router 2 port 1
|
||||
router_2.configure_port(1, "192.168.1.2", "255.255.255.252")
|
||||
router_1.configure_port(1, "192.168.1.1", "255.255.255.252")
|
||||
network.connect(router_1.ethernet_ports[1], router_2.ethernet_ports[1])
|
||||
router_1.enable_port(1)
|
||||
router_2.enable_port(1)
|
||||
return network
|
||||
|
||||
|
||||
def test_ping_default_gateway(pc_a_pc_b_router_1):
|
||||
pc_a, pc_b, router_1 = pc_a_pc_b_router_1
|
||||
|
||||
@@ -50,3 +118,68 @@ def test_host_on_other_subnet(pc_a_pc_b_router_1):
|
||||
pc_a, pc_b, router_1 = pc_a_pc_b_router_1
|
||||
|
||||
assert pc_a.ping("192.168.1.10")
|
||||
|
||||
|
||||
def test_no_route_no_ping(multi_hop_network):
|
||||
pc_a = multi_hop_network.get_node_by_hostname("pc_a")
|
||||
pc_b = multi_hop_network.get_node_by_hostname("pc_b")
|
||||
|
||||
assert not pc_a.ping(pc_b.ethernet_port[1].ip_address)
|
||||
|
||||
|
||||
def test_with_routes_can_ping(multi_hop_network):
|
||||
pc_a = multi_hop_network.get_node_by_hostname("pc_a")
|
||||
pc_b = multi_hop_network.get_node_by_hostname("pc_b")
|
||||
|
||||
router_1: Router = multi_hop_network.get_node_by_hostname("router_1") # noqa
|
||||
router_2: Router = multi_hop_network.get_node_by_hostname("router_2") # noqa
|
||||
|
||||
# Configure Route from Router 1 to PC B subnet
|
||||
router_1.route_table.add_route(
|
||||
address="192.168.2.0", subnet_mask="255.255.255.0", next_hop_ip_address="192.168.1.2"
|
||||
)
|
||||
|
||||
# Configure Route from Router 2 to PC A subnet
|
||||
router_2.route_table.add_route(
|
||||
address="192.168.0.2", subnet_mask="255.255.255.0", next_hop_ip_address="192.168.1.1"
|
||||
)
|
||||
|
||||
assert pc_a.ping(pc_b.ethernet_port[1].ip_address)
|
||||
|
||||
|
||||
def test_routing_services(multi_hop_network):
|
||||
pc_a = multi_hop_network.get_node_by_hostname("pc_a")
|
||||
|
||||
pc_b = multi_hop_network.get_node_by_hostname("pc_b")
|
||||
|
||||
pc_a.software_manager.install(NTPClient)
|
||||
ntp_client = pc_a.software_manager.software["NTPClient"]
|
||||
ntp_client.start()
|
||||
|
||||
pc_b.software_manager.install(NTPServer)
|
||||
pc_b.software_manager.software["NTPServer"].start()
|
||||
|
||||
ntp_client.configure(ntp_server_ip_address=pc_b.ethernet_port[1].ip_address)
|
||||
|
||||
router_1: Router = multi_hop_network.get_node_by_hostname("router_1") # noqa
|
||||
router_2: Router = multi_hop_network.get_node_by_hostname("router_2") # noqa
|
||||
|
||||
router_1.acl.add_rule(action=ACLAction.PERMIT, src_port=Port.NTP, dst_port=Port.NTP, position=21)
|
||||
router_2.acl.add_rule(action=ACLAction.PERMIT, src_port=Port.NTP, dst_port=Port.NTP, position=21)
|
||||
|
||||
assert ntp_client.time is None
|
||||
ntp_client.request_time()
|
||||
assert ntp_client.time is None
|
||||
|
||||
# Configure Route from Router 1 to PC B subnet
|
||||
router_1.route_table.add_route(
|
||||
address="192.168.2.0", subnet_mask="255.255.255.0", next_hop_ip_address="192.168.1.2"
|
||||
)
|
||||
|
||||
# Configure Route from Router 2 to PC A subnet
|
||||
router_2.route_table.add_route(
|
||||
address="192.168.0.2", subnet_mask="255.255.255.0", next_hop_ip_address="192.168.1.1"
|
||||
)
|
||||
|
||||
ntp_client.request_time()
|
||||
assert ntp_client.time is not None
|
||||
|
||||
@@ -90,7 +90,7 @@ def test_repeating_dos_attack(dos_bot_and_db_server):
|
||||
assert db_server_service.health_state_actual is SoftwareHealthState.OVERWHELMED
|
||||
|
||||
db_server_service.clear_connections()
|
||||
db_server_service.health_state_actual = SoftwareHealthState.GOOD
|
||||
db_server_service.set_health_state(SoftwareHealthState.GOOD)
|
||||
assert len(db_server_service.connections) == 0
|
||||
|
||||
computer.apply_timestep(timestep=1)
|
||||
@@ -121,7 +121,7 @@ def test_non_repeating_dos_attack(dos_bot_and_db_server):
|
||||
assert db_server_service.health_state_actual is SoftwareHealthState.OVERWHELMED
|
||||
|
||||
db_server_service.clear_connections()
|
||||
db_server_service.health_state_actual = SoftwareHealthState.GOOD
|
||||
db_server_service.set_health_state(SoftwareHealthState.GOOD)
|
||||
assert len(db_server_service.connections) == 0
|
||||
|
||||
computer.apply_timestep(timestep=1)
|
||||
|
||||
@@ -24,8 +24,8 @@ def populated_node(application_class) -> Tuple[Application, Computer]:
|
||||
return app, computer
|
||||
|
||||
|
||||
def test_service_on_offline_node(application_class):
|
||||
"""Test to check that the service cannot be interacted with when node it is on is off."""
|
||||
def test_application_on_offline_node(application_class):
|
||||
"""Test to check that the application cannot be interacted with when node it is on is off."""
|
||||
computer: Computer = Computer(
|
||||
hostname="test_computer",
|
||||
ip_address="192.168.1.2",
|
||||
@@ -49,8 +49,8 @@ def test_service_on_offline_node(application_class):
|
||||
assert app.operating_state is ApplicationOperatingState.CLOSED
|
||||
|
||||
|
||||
def test_server_turns_off_service(populated_node):
|
||||
"""Check that the service is turned off when the server is turned off"""
|
||||
def test_server_turns_off_application(populated_node):
|
||||
"""Check that the application is turned off when the server is turned off"""
|
||||
app, computer = populated_node
|
||||
|
||||
assert computer.operating_state is NodeOperatingState.ON
|
||||
@@ -65,8 +65,8 @@ def test_server_turns_off_service(populated_node):
|
||||
assert app.operating_state is ApplicationOperatingState.CLOSED
|
||||
|
||||
|
||||
def test_service_cannot_be_turned_on_when_server_is_off(populated_node):
|
||||
"""Check that the service cannot be started when the server is off."""
|
||||
def test_application_cannot_be_turned_on_when_computer_is_off(populated_node):
|
||||
"""Check that the application cannot be started when the computer is off."""
|
||||
app, computer = populated_node
|
||||
|
||||
assert computer.operating_state is NodeOperatingState.ON
|
||||
@@ -86,8 +86,8 @@ def test_service_cannot_be_turned_on_when_server_is_off(populated_node):
|
||||
assert app.operating_state is ApplicationOperatingState.CLOSED
|
||||
|
||||
|
||||
def test_server_turns_on_service(populated_node):
|
||||
"""Check that turning on the server turns on service."""
|
||||
def test_computer_runs_applications(populated_node):
|
||||
"""Check that turning on the computer will turn on applications."""
|
||||
app, computer = populated_node
|
||||
|
||||
assert computer.operating_state is NodeOperatingState.ON
|
||||
@@ -109,13 +109,14 @@ def test_server_turns_on_service(populated_node):
|
||||
assert computer.operating_state is NodeOperatingState.ON
|
||||
assert app.operating_state is ApplicationOperatingState.RUNNING
|
||||
|
||||
computer.start_up_duration = 0
|
||||
computer.shut_down_duration = 0
|
||||
|
||||
computer.power_off()
|
||||
for i in range(computer.start_up_duration + 1):
|
||||
computer.apply_timestep(timestep=i)
|
||||
assert computer.operating_state is NodeOperatingState.OFF
|
||||
assert app.operating_state is ApplicationOperatingState.CLOSED
|
||||
|
||||
computer.power_on()
|
||||
for i in range(computer.start_up_duration + 1):
|
||||
computer.apply_timestep(timestep=i)
|
||||
assert computer.operating_state is NodeOperatingState.ON
|
||||
assert app.operating_state is ApplicationOperatingState.RUNNING
|
||||
|
||||
@@ -117,13 +117,14 @@ def test_server_turns_on_service(populated_node):
|
||||
assert server.operating_state is NodeOperatingState.ON
|
||||
assert service.operating_state is ServiceOperatingState.RUNNING
|
||||
|
||||
server.start_up_duration = 0
|
||||
server.shut_down_duration = 0
|
||||
|
||||
server.power_off()
|
||||
for i in range(server.start_up_duration + 1):
|
||||
server.apply_timestep(timestep=i)
|
||||
assert server.operating_state is NodeOperatingState.OFF
|
||||
assert service.operating_state is ServiceOperatingState.STOPPED
|
||||
|
||||
server.power_on()
|
||||
for i in range(server.start_up_duration + 1):
|
||||
server.apply_timestep(timestep=i)
|
||||
assert server.operating_state is NodeOperatingState.ON
|
||||
assert service.operating_state is ServiceOperatingState.RUNNING
|
||||
|
||||
@@ -53,12 +53,12 @@ def test_node_os_scan(node, service, application):
|
||||
# TODO implement processes
|
||||
|
||||
# add services to node
|
||||
service.health_state_actual = SoftwareHealthState.COMPROMISED
|
||||
service.set_health_state(SoftwareHealthState.COMPROMISED)
|
||||
node.install_service(service=service)
|
||||
assert service.health_state_visible == SoftwareHealthState.UNUSED
|
||||
|
||||
# add application to node
|
||||
application.health_state_actual = SoftwareHealthState.COMPROMISED
|
||||
application.set_health_state(SoftwareHealthState.COMPROMISED)
|
||||
node.install_application(application=application)
|
||||
assert application.health_state_visible == SoftwareHealthState.UNUSED
|
||||
|
||||
@@ -101,7 +101,7 @@ def test_node_red_scan(node, service, application):
|
||||
assert service.revealed_to_red is False
|
||||
|
||||
# add application to node
|
||||
application.health_state_actual = SoftwareHealthState.COMPROMISED
|
||||
application.set_health_state(SoftwareHealthState.COMPROMISED)
|
||||
node.install_application(application=application)
|
||||
assert application.revealed_to_red is False
|
||||
|
||||
|
||||
@@ -10,6 +10,22 @@ from primaite.simulator.system.applications.database_client import DatabaseClien
|
||||
from primaite.simulator.system.services.database.database_service import DatabaseService
|
||||
|
||||
|
||||
def filter_keys_nested_item(data, keys):
|
||||
stack = [(data, {})]
|
||||
while stack:
|
||||
current, filtered = stack.pop()
|
||||
if isinstance(current, dict):
|
||||
for k, v in current.items():
|
||||
if k in keys:
|
||||
filtered[k] = filter_keys_nested_item(v, keys)
|
||||
elif isinstance(v, (dict, list)):
|
||||
stack.append((v, {}))
|
||||
elif isinstance(current, list):
|
||||
for item in current:
|
||||
stack.append((item, {}))
|
||||
return filtered
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def network(example_network) -> Network:
|
||||
assert len(example_network.routers) is 1
|
||||
@@ -59,10 +75,10 @@ def test_reset_network(network):
|
||||
|
||||
assert client_1.operating_state is NodeOperatingState.ON
|
||||
assert server_1.operating_state is NodeOperatingState.ON
|
||||
|
||||
assert json.dumps(network.describe_state(), sort_keys=True, indent=2) == json.dumps(
|
||||
state_before, sort_keys=True, indent=2
|
||||
)
|
||||
# don't worry if UUIDs change
|
||||
a = filter_keys_nested_item(json.dumps(network.describe_state(), sort_keys=True, indent=2), ["uuid"])
|
||||
b = filter_keys_nested_item(json.dumps(state_before, sort_keys=True, indent=2), ["uuid"])
|
||||
assert a == b
|
||||
|
||||
|
||||
def test_creating_container():
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
from primaite.simulator.system.applications.application import ApplicationOperatingState
|
||||
from primaite.simulator.system.software import SoftwareHealthState
|
||||
|
||||
|
||||
def test_scan(application):
|
||||
assert application.operating_state == ApplicationOperatingState.CLOSED
|
||||
assert application.health_state_visible == SoftwareHealthState.UNUSED
|
||||
|
||||
application.run()
|
||||
assert application.operating_state == ApplicationOperatingState.RUNNING
|
||||
assert application.health_state_visible == SoftwareHealthState.UNUSED
|
||||
|
||||
application.scan()
|
||||
assert application.operating_state == ApplicationOperatingState.RUNNING
|
||||
assert application.health_state_visible == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_run_application(application):
|
||||
assert application.operating_state == ApplicationOperatingState.CLOSED
|
||||
assert application.health_state_actual == SoftwareHealthState.UNUSED
|
||||
|
||||
application.run()
|
||||
assert application.operating_state == ApplicationOperatingState.RUNNING
|
||||
assert application.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_close_application(application):
|
||||
application.run()
|
||||
assert application.operating_state == ApplicationOperatingState.RUNNING
|
||||
assert application.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
application.close()
|
||||
assert application.operating_state == ApplicationOperatingState.CLOSED
|
||||
assert application.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_application_describe_states(application):
|
||||
assert application.operating_state == ApplicationOperatingState.CLOSED
|
||||
assert application.health_state_actual == SoftwareHealthState.UNUSED
|
||||
|
||||
assert SoftwareHealthState.UNUSED.value == application.describe_state().get("health_state_actual")
|
||||
|
||||
application.run()
|
||||
assert SoftwareHealthState.GOOD.value == application.describe_state().get("health_state_actual")
|
||||
|
||||
application.set_health_state(SoftwareHealthState.COMPROMISED)
|
||||
assert SoftwareHealthState.COMPROMISED.value == application.describe_state().get("health_state_actual")
|
||||
|
||||
application.patch()
|
||||
assert SoftwareHealthState.PATCHING.value == application.describe_state().get("health_state_actual")
|
||||
@@ -2,6 +2,7 @@ from ipaddress import IPv4Address
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus
|
||||
from primaite.simulator.network.hardware.base import Node
|
||||
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
|
||||
from primaite.simulator.network.hardware.nodes.computer import Computer
|
||||
@@ -42,6 +43,7 @@ def test_ftp_client_store_file(ftp_client):
|
||||
"dest_folder_name": "downloads",
|
||||
"dest_file_name": "file.txt",
|
||||
"file_size": 24,
|
||||
"health_status": FileSystemItemHealthStatus.GOOD,
|
||||
},
|
||||
packet_payload_size=24,
|
||||
status_code=FTPStatusCode.OK,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus
|
||||
from primaite.simulator.network.hardware.base import Node
|
||||
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
|
||||
from primaite.simulator.network.hardware.nodes.server import Server
|
||||
@@ -41,6 +42,7 @@ def test_ftp_server_store_file(ftp_server):
|
||||
"dest_folder_name": "downloads",
|
||||
"dest_file_name": "file.txt",
|
||||
"file_size": 24,
|
||||
"health_status": FileSystemItemHealthStatus.GOOD,
|
||||
},
|
||||
packet_payload_size=24,
|
||||
)
|
||||
|
||||
@@ -19,55 +19,146 @@ def test_scan(service):
|
||||
|
||||
def test_start_service(service):
|
||||
assert service.operating_state == ServiceOperatingState.STOPPED
|
||||
assert service.health_state_actual == SoftwareHealthState.UNUSED
|
||||
service.start()
|
||||
|
||||
assert service.operating_state == ServiceOperatingState.RUNNING
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_stop_service(service):
|
||||
service.start()
|
||||
assert service.operating_state == ServiceOperatingState.RUNNING
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
service.stop()
|
||||
assert service.operating_state == ServiceOperatingState.STOPPED
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_pause_and_resume_service(service):
|
||||
assert service.operating_state == ServiceOperatingState.STOPPED
|
||||
service.resume()
|
||||
assert service.operating_state == ServiceOperatingState.STOPPED
|
||||
assert service.health_state_actual == SoftwareHealthState.UNUSED
|
||||
|
||||
service.start()
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
service.pause()
|
||||
assert service.operating_state == ServiceOperatingState.PAUSED
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
service.resume()
|
||||
assert service.operating_state == ServiceOperatingState.RUNNING
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_restart(service):
|
||||
assert service.operating_state == ServiceOperatingState.STOPPED
|
||||
assert service.health_state_actual == SoftwareHealthState.UNUSED
|
||||
service.restart()
|
||||
# Service is STOPPED. Restart will only work if the service was PAUSED or RUNNING
|
||||
assert service.operating_state == ServiceOperatingState.STOPPED
|
||||
assert service.health_state_actual == SoftwareHealthState.UNUSED
|
||||
|
||||
service.start()
|
||||
assert service.operating_state == ServiceOperatingState.RUNNING
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
service.restart()
|
||||
# Service is RUNNING. Restart should work
|
||||
assert service.operating_state == ServiceOperatingState.RESTARTING
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
timestep = 0
|
||||
while service.operating_state == ServiceOperatingState.RESTARTING:
|
||||
service.apply_timestep(timestep)
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
timestep += 1
|
||||
|
||||
assert service.operating_state == ServiceOperatingState.RUNNING
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_restart_compromised(service):
|
||||
service.start()
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
# compromise the service
|
||||
service.set_health_state(SoftwareHealthState.COMPROMISED)
|
||||
|
||||
service.restart()
|
||||
assert service.operating_state == ServiceOperatingState.RESTARTING
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
|
||||
"""
|
||||
Service should be compromised even after reset.
|
||||
|
||||
Only way to remove compromised status is via patching.
|
||||
"""
|
||||
|
||||
timestep = 0
|
||||
while service.operating_state == ServiceOperatingState.RESTARTING:
|
||||
service.apply_timestep(timestep)
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
timestep += 1
|
||||
|
||||
assert service.operating_state == ServiceOperatingState.RUNNING
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
|
||||
|
||||
def test_compromised_service_remains_compromised(service):
|
||||
"""
|
||||
Tests that a compromised service stays compromised.
|
||||
|
||||
The only way that the service can be uncompromised is by running patch.
|
||||
"""
|
||||
service.start()
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
service.set_health_state(SoftwareHealthState.COMPROMISED)
|
||||
|
||||
service.stop()
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
|
||||
service.start()
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
|
||||
service.disable()
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
|
||||
service.enable()
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
|
||||
service.pause()
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
|
||||
service.resume()
|
||||
assert service.health_state_actual == SoftwareHealthState.COMPROMISED
|
||||
|
||||
|
||||
def test_service_patching(service):
|
||||
service.start()
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
service.set_health_state(SoftwareHealthState.COMPROMISED)
|
||||
|
||||
service.patch()
|
||||
assert service.health_state_actual == SoftwareHealthState.PATCHING
|
||||
|
||||
for i in range(service.patching_duration + 1):
|
||||
service.apply_timestep(i)
|
||||
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_enable_disable(service):
|
||||
service.disable()
|
||||
assert service.operating_state == ServiceOperatingState.DISABLED
|
||||
assert service.health_state_actual == SoftwareHealthState.UNUSED
|
||||
|
||||
service.enable()
|
||||
assert service.operating_state == ServiceOperatingState.STOPPED
|
||||
assert service.health_state_actual == SoftwareHealthState.UNUSED
|
||||
|
||||
|
||||
def test_overwhelm_service(service):
|
||||
@@ -76,13 +167,13 @@ def test_overwhelm_service(service):
|
||||
|
||||
uuid = str(uuid4())
|
||||
assert service.add_connection(connection_id=uuid) # should be true
|
||||
assert service.health_state_actual is SoftwareHealthState.GOOD
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
assert not service.add_connection(connection_id=uuid) # fails because connection already exists
|
||||
assert service.health_state_actual is SoftwareHealthState.GOOD
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
assert service.add_connection(connection_id=str(uuid4())) # succeed
|
||||
assert service.health_state_actual is SoftwareHealthState.GOOD
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
assert not service.add_connection(connection_id=str(uuid4())) # fail because at capacity
|
||||
assert service.health_state_actual is SoftwareHealthState.OVERWHELMED
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
|
||||
from primaite.simulator.network.hardware.nodes.server import Server
|
||||
from primaite.simulator.network.protocols.http import (
|
||||
HttpRequestMethod,
|
||||
@@ -15,7 +16,11 @@ from primaite.simulator.system.services.web_server.web_server import WebServer
|
||||
@pytest.fixture(scope="function")
|
||||
def web_server() -> Server:
|
||||
node = Server(
|
||||
hostname="web_server", ip_address="192.168.1.10", subnet_mask="255.255.255.0", default_gateway="192.168.1.1"
|
||||
hostname="web_server",
|
||||
ip_address="192.168.1.10",
|
||||
subnet_mask="255.255.255.0",
|
||||
default_gateway="192.168.1.1",
|
||||
operating_state=NodeOperatingState.ON,
|
||||
)
|
||||
node.software_manager.install(software_class=WebServer)
|
||||
node.software_manager.software.get("WebServer").start()
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from typing import Dict
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.core.sys_log import SysLog
|
||||
from primaite.simulator.system.software import Software, SoftwareHealthState
|
||||
|
||||
|
||||
class TestSoftware(Software):
|
||||
def describe_state(self) -> Dict:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def software(file_system):
|
||||
return TestSoftware(
|
||||
name="TestSoftware", port=Port.ARP, file_system=file_system, sys_log=SysLog(hostname="test_service")
|
||||
)
|
||||
|
||||
|
||||
def test_software_creation(software):
|
||||
assert software is not None
|
||||
|
||||
|
||||
def test_software_set_health_state(software):
|
||||
assert software.health_state_actual == SoftwareHealthState.UNUSED
|
||||
software.set_health_state(SoftwareHealthState.GOOD)
|
||||
assert software.health_state_actual == SoftwareHealthState.GOOD
|
||||
Reference in New Issue
Block a user