Merge branch 'dev' into bugfix/2299-check_hash_function_corrupts_files_and_folders
This commit is contained in:
@@ -8,6 +8,7 @@ io_settings:
|
||||
save_step_metadata: false
|
||||
save_pcap_logs: true
|
||||
save_sys_logs: true
|
||||
sys_log_level: WARNING
|
||||
|
||||
|
||||
game:
|
||||
@@ -60,6 +61,102 @@ agents:
|
||||
frequency: 4
|
||||
variance: 3
|
||||
|
||||
|
||||
|
||||
- ref: defender
|
||||
team: BLUE
|
||||
type: ProxyAgent
|
||||
|
||||
observation_space:
|
||||
type: CUSTOM
|
||||
options:
|
||||
components:
|
||||
- type: NODES
|
||||
label: NODES
|
||||
options:
|
||||
hosts:
|
||||
- hostname: client_1
|
||||
- hostname: client_2
|
||||
- hostname: client_3
|
||||
num_services: 1
|
||||
num_applications: 0
|
||||
num_folders: 1
|
||||
num_files: 1
|
||||
num_nics: 2
|
||||
include_num_access: false
|
||||
include_nmne: true
|
||||
routers:
|
||||
- hostname: router_1
|
||||
num_ports: 0
|
||||
ip_list:
|
||||
- 192.168.10.21
|
||||
- 192.168.10.22
|
||||
- 192.168.10.23
|
||||
wildcard_list:
|
||||
- 0.0.0.1
|
||||
port_list:
|
||||
- 80
|
||||
- 5432
|
||||
protocol_list:
|
||||
- ICMP
|
||||
- TCP
|
||||
- UDP
|
||||
num_rules: 10
|
||||
|
||||
- type: LINKS
|
||||
label: LINKS
|
||||
options:
|
||||
link_references:
|
||||
- switch_1:eth-1<->client_1:eth-1
|
||||
- switch_1:eth-2<->client_2:eth-1
|
||||
- type: "NONE"
|
||||
label: ICS
|
||||
options: {}
|
||||
|
||||
action_space:
|
||||
action_list:
|
||||
- type: DONOTHING
|
||||
|
||||
action_map:
|
||||
0:
|
||||
action: DONOTHING
|
||||
options: {}
|
||||
options:
|
||||
nodes:
|
||||
- node_name: switch
|
||||
- node_name: client_1
|
||||
- node_name: client_2
|
||||
- node_name: client_3
|
||||
max_folders_per_node: 2
|
||||
max_files_per_folder: 2
|
||||
max_services_per_node: 2
|
||||
max_nics_per_node: 8
|
||||
max_acl_rules: 10
|
||||
ip_list:
|
||||
- 192.168.10.21
|
||||
- 192.168.10.22
|
||||
- 192.168.10.23
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
- type: DATABASE_FILE_INTEGRITY
|
||||
weight: 0.5
|
||||
options:
|
||||
node_hostname: database_server
|
||||
folder_name: database
|
||||
file_name: database.db
|
||||
|
||||
|
||||
- type: WEB_SERVER_404_PENALTY
|
||||
weight: 0.5
|
||||
options:
|
||||
node_hostname: web_server
|
||||
service_name: web_server_web_service
|
||||
|
||||
|
||||
agent_settings:
|
||||
flatten_obs: true
|
||||
|
||||
simulation:
|
||||
network:
|
||||
nodes:
|
||||
@@ -75,6 +172,7 @@ simulation:
|
||||
default_gateway: 192.168.10.1
|
||||
dns_server: 192.168.1.10
|
||||
applications:
|
||||
- type: RansomwareScript
|
||||
- type: WebBrowser
|
||||
options:
|
||||
target_url: http://arcd.com/users/
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# No green agents present
|
||||
greens: &greens []
|
||||
@@ -0,0 +1,34 @@
|
||||
agents: &greens
|
||||
- ref: green_A
|
||||
team: GREEN
|
||||
type: ProbabilisticAgent
|
||||
agent_settings:
|
||||
action_probabilities:
|
||||
0: 0.2
|
||||
1: 0.8
|
||||
observation_space: null
|
||||
action_space:
|
||||
action_list:
|
||||
- type: DONOTHING
|
||||
- type: NODE_APPLICATION_EXECUTE
|
||||
options:
|
||||
nodes:
|
||||
- node_name: client
|
||||
applications:
|
||||
- application_name: DatabaseClient
|
||||
action_map:
|
||||
0:
|
||||
action: DONOTHING
|
||||
options: {}
|
||||
1:
|
||||
action: NODE_APPLICATION_EXECUTE
|
||||
options:
|
||||
node_id: 0
|
||||
application_id: 0
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
- type: GREEN_ADMIN_DATABASE_UNREACHABLE_PENALTY
|
||||
weight: 1.0
|
||||
options:
|
||||
node_hostname: client
|
||||
@@ -0,0 +1,34 @@
|
||||
agents: &greens
|
||||
- ref: green_B
|
||||
team: GREEN
|
||||
type: ProbabilisticAgent
|
||||
agent_settings:
|
||||
action_probabilities:
|
||||
0: 0.95
|
||||
1: 0.05
|
||||
observation_space: null
|
||||
action_space:
|
||||
action_list:
|
||||
- type: DONOTHING
|
||||
- type: NODE_APPLICATION_EXECUTE
|
||||
options:
|
||||
nodes:
|
||||
- node_name: client
|
||||
applications:
|
||||
- application_name: DatabaseClient
|
||||
action_map:
|
||||
0:
|
||||
action: DONOTHING
|
||||
options: {}
|
||||
1:
|
||||
action: NODE_APPLICATION_EXECUTE
|
||||
options:
|
||||
node_id: 0
|
||||
application_id: 0
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
- type: GREEN_ADMIN_DATABASE_UNREACHABLE_PENALTY
|
||||
weight: 1.0
|
||||
options:
|
||||
node_hostname: client
|
||||
@@ -0,0 +1,2 @@
|
||||
# No red agents present
|
||||
reds: &reds []
|
||||
26
tests/assets/configs/scenario_with_placeholders/reds_1.yaml
Normal file
26
tests/assets/configs/scenario_with_placeholders/reds_1.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
reds: &reds
|
||||
- ref: red_A
|
||||
team: RED
|
||||
type: RedDatabaseCorruptingAgent
|
||||
|
||||
observation_space: null
|
||||
|
||||
action_space:
|
||||
action_list:
|
||||
- type: DONOTHING
|
||||
- type: NODE_APPLICATION_EXECUTE
|
||||
options:
|
||||
nodes:
|
||||
- node_name: client
|
||||
applications:
|
||||
- application_name: DataManipulationBot
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
- type: DUMMY
|
||||
|
||||
agent_settings:
|
||||
start_settings:
|
||||
start_step: 10
|
||||
frequency: 10
|
||||
variance: 0
|
||||
26
tests/assets/configs/scenario_with_placeholders/reds_2.yaml
Normal file
26
tests/assets/configs/scenario_with_placeholders/reds_2.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
reds: &reds
|
||||
- ref: red_B
|
||||
team: RED
|
||||
type: RedDatabaseCorruptingAgent
|
||||
|
||||
observation_space: null
|
||||
|
||||
action_space:
|
||||
action_list:
|
||||
- type: DONOTHING
|
||||
- type: NODE_APPLICATION_EXECUTE
|
||||
options:
|
||||
nodes:
|
||||
- node_name: client
|
||||
applications:
|
||||
- application_name: DataManipulationBot
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
- type: DUMMY
|
||||
|
||||
agent_settings:
|
||||
start_settings:
|
||||
start_step: 3
|
||||
frequency: 2
|
||||
variance: 1
|
||||
168
tests/assets/configs/scenario_with_placeholders/scenario.yaml
Normal file
168
tests/assets/configs/scenario_with_placeholders/scenario.yaml
Normal file
@@ -0,0 +1,168 @@
|
||||
io_settings:
|
||||
save_agent_actions: true
|
||||
save_step_metadata: false
|
||||
save_pcap_logs: false
|
||||
save_sys_logs: false
|
||||
|
||||
|
||||
game:
|
||||
max_episode_length: 128
|
||||
ports:
|
||||
- HTTP
|
||||
- POSTGRES_SERVER
|
||||
protocols:
|
||||
- ICMP
|
||||
- TCP
|
||||
- UDP
|
||||
thresholds:
|
||||
nmne:
|
||||
high: 10
|
||||
medium: 5
|
||||
low: 0
|
||||
|
||||
agents:
|
||||
- *greens
|
||||
- *reds
|
||||
|
||||
- ref: defender
|
||||
team: BLUE
|
||||
type: ProxyAgent
|
||||
observation_space:
|
||||
type: CUSTOM
|
||||
options:
|
||||
components:
|
||||
- type: NODES
|
||||
label: NODES
|
||||
options:
|
||||
routers: []
|
||||
hosts:
|
||||
- hostname: client
|
||||
- hostname: server
|
||||
num_services: 1
|
||||
num_applications: 1
|
||||
num_folders: 1
|
||||
num_files: 1
|
||||
num_nics: 1
|
||||
include_num_access: false
|
||||
include_nmne: true
|
||||
|
||||
- type: LINKS
|
||||
label: LINKS
|
||||
options:
|
||||
link_references:
|
||||
- client:eth-1<->switch_1:eth-1
|
||||
- server:eth-1<->switch_1:eth-2
|
||||
|
||||
action_space:
|
||||
action_list:
|
||||
- type: DONOTHING
|
||||
- type: NODE_SHUTDOWN
|
||||
- type: NODE_STARTUP
|
||||
- type: HOST_NIC_ENABLE
|
||||
- type: HOST_NIC_DISABLE
|
||||
action_map:
|
||||
0:
|
||||
action: DONOTHING
|
||||
options: {}
|
||||
1:
|
||||
action: NODE_SHUTDOWN
|
||||
options:
|
||||
node_id: 0
|
||||
2:
|
||||
action: NODE_SHUTDOWN
|
||||
options:
|
||||
node_id: 1
|
||||
3:
|
||||
action: NODE_STARTUP
|
||||
options:
|
||||
node_id: 0
|
||||
4:
|
||||
action: NODE_STARTUP
|
||||
options:
|
||||
node_id: 1
|
||||
5:
|
||||
action: HOST_NIC_DISABLE
|
||||
options:
|
||||
node_id: 0
|
||||
nic_id: 0
|
||||
6:
|
||||
action: HOST_NIC_DISABLE
|
||||
options:
|
||||
node_id: 1
|
||||
nic_id: 0
|
||||
7:
|
||||
action: HOST_NIC_ENABLE
|
||||
options:
|
||||
node_id: 0
|
||||
nic_id: 0
|
||||
8:
|
||||
action: HOST_NIC_ENABLE
|
||||
options:
|
||||
node_id: 1
|
||||
nic_id: 0
|
||||
options:
|
||||
nodes:
|
||||
- node_name: client
|
||||
- node_name: server
|
||||
|
||||
max_folders_per_node: 0
|
||||
max_files_per_folder: 0
|
||||
max_services_per_node: 0
|
||||
max_nics_per_node: 1
|
||||
max_acl_rules: 0
|
||||
ip_list:
|
||||
- 192.168.1.2
|
||||
- 192.168.1.3
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
- type: DATABASE_FILE_INTEGRITY
|
||||
weight: 0.40
|
||||
options:
|
||||
node_hostname: database_server
|
||||
folder_name: database
|
||||
file_name: database.db
|
||||
|
||||
agent_settings:
|
||||
flatten_obs: false
|
||||
|
||||
|
||||
simulation:
|
||||
network:
|
||||
nodes:
|
||||
- hostname: client
|
||||
type: computer
|
||||
ip_address: 192.168.1.2
|
||||
subnet_mask: 255.255.255.0
|
||||
default_gateway: 192.168.1.1
|
||||
applications:
|
||||
- type: DatabaseClient
|
||||
options:
|
||||
db_server_ip: 192.168.1.3
|
||||
- type: DataManipulationBot
|
||||
options:
|
||||
server_ip: 192.168.1.3
|
||||
payload: "DELETE"
|
||||
|
||||
- hostname: switch_1
|
||||
type: switch
|
||||
num_ports: 2
|
||||
|
||||
- hostname: server
|
||||
type: server
|
||||
ip_address: 192.168.1.3
|
||||
subnet_mask: 255.255.255.0
|
||||
default_gateway: 192.168.1.1
|
||||
services:
|
||||
- type: DatabaseService
|
||||
|
||||
links:
|
||||
- endpoint_a_hostname: client
|
||||
endpoint_a_port: 1
|
||||
endpoint_b_hostname: switch_1
|
||||
endpoint_b_port: 1
|
||||
|
||||
- endpoint_a_hostname: server
|
||||
endpoint_a_port: 1
|
||||
endpoint_b_hostname: switch_1
|
||||
endpoint_b_port: 2
|
||||
@@ -0,0 +1,14 @@
|
||||
base_scenario: scenario.yaml
|
||||
schedule:
|
||||
0:
|
||||
- greens_0.yaml
|
||||
- reds_0.yaml
|
||||
1:
|
||||
- greens_0.yaml
|
||||
- reds_1.yaml
|
||||
2:
|
||||
- greens_1.yaml
|
||||
- reds_1.yaml
|
||||
3:
|
||||
- greens_2.yaml
|
||||
- reds_2.yaml
|
||||
@@ -16,7 +16,7 @@ def test_sb3_compatibility():
|
||||
with open(data_manipulation_config_path(), "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
|
||||
gym = PrimaiteGymEnv(game_config=cfg)
|
||||
gym = PrimaiteGymEnv(env_config=cfg)
|
||||
model = PPO("MlpPolicy", gym)
|
||||
|
||||
model.learn(total_timesteps=1000)
|
||||
|
||||
@@ -21,7 +21,7 @@ class TestPrimaiteEnvironment:
|
||||
"""Check that environment loads correctly from config and it can be reset."""
|
||||
with open(CFG_PATH, "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
env = PrimaiteGymEnv(game_config=cfg)
|
||||
env = PrimaiteGymEnv(env_config=cfg)
|
||||
|
||||
def env_checks():
|
||||
assert env is not None
|
||||
@@ -44,7 +44,7 @@ class TestPrimaiteEnvironment:
|
||||
"""Make sure you can go all the way through the session without errors."""
|
||||
with open(CFG_PATH, "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
env = PrimaiteGymEnv(game_config=cfg)
|
||||
env = PrimaiteGymEnv(env_config=cfg)
|
||||
|
||||
assert (num_actions := len(env.agent.action_manager.action_map)) == 54
|
||||
# run every action and make sure there's no crash
|
||||
@@ -88,4 +88,4 @@ class TestPrimaiteEnvironment:
|
||||
with open(MISCONFIGURED_PATH, "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
with pytest.raises(pydantic.ValidationError):
|
||||
env = PrimaiteGymEnv(game_config=cfg)
|
||||
env = PrimaiteGymEnv(env_config=cfg)
|
||||
|
||||
@@ -4,7 +4,7 @@ from primaite.game.game import PrimaiteGame
|
||||
from primaite.session.environment import PrimaiteGymEnv
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
from primaite.simulator.network.hardware.nodes.host.server import Server
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient, DatabaseClientConnection
|
||||
from primaite.simulator.system.applications.red_applications.data_manipulation_bot import DataManipulationBot
|
||||
from primaite.simulator.system.services.database.database_service import DatabaseService
|
||||
from tests import TEST_ASSETS_ROOT
|
||||
@@ -20,23 +20,23 @@ def test_data_manipulation(uc2_network):
|
||||
|
||||
web_server: Server = uc2_network.get_node_by_hostname("web_server")
|
||||
db_client: DatabaseClient = web_server.software_manager.software.get("DatabaseClient")
|
||||
|
||||
db_connection: DatabaseClientConnection = db_client.get_new_connection()
|
||||
db_service.backup_database()
|
||||
|
||||
# First check that the DB client on the web_server can successfully query the users table on the database
|
||||
assert db_client.query("SELECT")
|
||||
assert db_connection.query("SELECT")
|
||||
|
||||
# Now we run the DataManipulationBot
|
||||
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")
|
||||
assert not db_connection.query("SELECT")
|
||||
|
||||
# Now restore the database
|
||||
db_service.restore_backup()
|
||||
|
||||
# Now check that the DB client on the web_server can successfully query the users table on the database
|
||||
assert db_client.query("SELECT")
|
||||
assert db_connection.query("SELECT")
|
||||
|
||||
|
||||
def test_application_install_uninstall_on_uc2():
|
||||
@@ -44,7 +44,7 @@ def test_application_install_uninstall_on_uc2():
|
||||
with open(TEST_ASSETS_ROOT / "configs/test_application_install.yaml", "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
|
||||
env = PrimaiteGymEnv(game_config=cfg)
|
||||
env = PrimaiteGymEnv(env_config=cfg)
|
||||
env.agent.flatten_obs = False
|
||||
env.reset()
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from primaite.session.environment import PrimaiteGymEnv, PrimaiteRayEnv, PrimaiteRayMARLEnv
|
||||
from tests.conftest import TEST_ASSETS_ROOT
|
||||
|
||||
folder_path = TEST_ASSETS_ROOT / "configs" / "scenario_with_placeholders"
|
||||
single_yaml_config = TEST_ASSETS_ROOT / "configs" / "test_primaite_session.yaml"
|
||||
with open(single_yaml_config, "r") as f:
|
||||
config_dict = yaml.safe_load(f)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("env_type", [PrimaiteGymEnv, PrimaiteRayEnv, PrimaiteRayMARLEnv])
|
||||
def test_creating_env_with_folder(env_type):
|
||||
"""Check that the environment can be created with a folder path."""
|
||||
|
||||
def check_taking_steps(e):
|
||||
if isinstance(e, PrimaiteRayMARLEnv):
|
||||
for i in range(9):
|
||||
e.step({k: i for k in e.game.rl_agents})
|
||||
else:
|
||||
for i in range(9):
|
||||
e.step(i)
|
||||
|
||||
env = env_type(env_config=folder_path)
|
||||
assert env is not None
|
||||
for _ in range(3): # do it multiple times to ensure it loops back to the beginning
|
||||
assert len(env.game.agents) == 1
|
||||
assert "defender" in env.game.agents
|
||||
check_taking_steps(env)
|
||||
|
||||
env.reset()
|
||||
assert len(env.game.agents) == 2
|
||||
assert "defender" in env.game.agents
|
||||
assert "red_A" in env.game.agents
|
||||
check_taking_steps(env)
|
||||
|
||||
env.reset()
|
||||
assert len(env.game.agents) == 3
|
||||
assert all([a in env.game.agents for a in ["defender", "green_A", "red_A"]])
|
||||
check_taking_steps(env)
|
||||
|
||||
env.reset()
|
||||
assert len(env.game.agents) == 3
|
||||
assert all([a in env.game.agents for a in ["defender", "green_B", "red_B"]])
|
||||
check_taking_steps(env)
|
||||
|
||||
env.reset()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"env_data, env_type",
|
||||
[
|
||||
(single_yaml_config, PrimaiteGymEnv),
|
||||
(single_yaml_config, PrimaiteRayEnv),
|
||||
(single_yaml_config, PrimaiteRayMARLEnv),
|
||||
(config_dict, PrimaiteGymEnv),
|
||||
(config_dict, PrimaiteRayEnv),
|
||||
(config_dict, PrimaiteRayMARLEnv),
|
||||
],
|
||||
)
|
||||
def test_creating_env_with_static_config(env_data, env_type):
|
||||
"""Check that the environment can be created with a single yaml file."""
|
||||
env = env_type(env_config=single_yaml_config)
|
||||
assert env is not None
|
||||
agents_before = len(env.game.agents)
|
||||
env.reset()
|
||||
assert len(env.game.agents) == agents_before
|
||||
@@ -0,0 +1,36 @@
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
|
||||
import yaml
|
||||
|
||||
from primaite.config.load import data_manipulation_config_path
|
||||
from primaite.game.game import PrimaiteGame
|
||||
from primaite.session.environment import PrimaiteGymEnv
|
||||
from primaite.simulator import LogLevel
|
||||
from tests import TEST_ASSETS_ROOT
|
||||
|
||||
BASIC_CONFIG = TEST_ASSETS_ROOT / "configs/basic_switched_network.yaml"
|
||||
|
||||
|
||||
def load_config(config_path: Union[str, Path]) -> PrimaiteGame:
|
||||
"""Returns a PrimaiteGame object which loads the contents of a given yaml path."""
|
||||
with open(config_path, "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
|
||||
return PrimaiteGame.from_config(cfg)
|
||||
|
||||
|
||||
def test_io_settings():
|
||||
"""Test that the io_settings are loaded correctly."""
|
||||
with open(BASIC_CONFIG, "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
env = PrimaiteGymEnv(env_config=cfg)
|
||||
|
||||
assert env.io.settings is not None
|
||||
|
||||
assert env.io.settings.sys_log_level is LogLevel.WARNING
|
||||
assert env.io.settings.save_pcap_logs
|
||||
assert env.io.settings.save_sys_logs
|
||||
assert env.io.settings.save_step_metadata is False
|
||||
|
||||
assert env.io.settings.write_sys_log_to_terminal is False # false by default
|
||||
@@ -507,7 +507,7 @@ def test_firewall_acl_add_remove_rule_integration():
|
||||
with open(FIREWALL_ACTIONS_NETWORK, "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
|
||||
env = PrimaiteGymEnv(game_config=cfg)
|
||||
env = PrimaiteGymEnv(env_config=cfg)
|
||||
|
||||
# 1: Check that traffic is normal and acl starts off with 4 rules.
|
||||
firewall = env.game.simulation.network.get_node_by_hostname("firewall")
|
||||
@@ -598,7 +598,7 @@ def test_firewall_port_disable_enable_integration():
|
||||
with open(FIREWALL_ACTIONS_NETWORK, "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
|
||||
env = PrimaiteGymEnv(game_config=cfg)
|
||||
env = PrimaiteGymEnv(env_config=cfg)
|
||||
firewall = env.game.simulation.network.get_node_by_hostname("firewall")
|
||||
|
||||
assert firewall.dmz_port.enabled == True
|
||||
|
||||
@@ -103,7 +103,7 @@ def test_shared_reward():
|
||||
with open(CFG_PATH, "r") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
|
||||
env = PrimaiteGymEnv(game_config=cfg)
|
||||
env = PrimaiteGymEnv(env_config=cfg)
|
||||
|
||||
env.reset()
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from primaite.game.agent.observations.nic_observations import NICObservation
|
||||
from primaite.simulator.network.hardware.nodes.host.server import Server
|
||||
from primaite.simulator.network.nmne import set_nmne_config
|
||||
from primaite.simulator.sim_container import Simulation
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient, DatabaseClientConnection
|
||||
|
||||
|
||||
def test_capture_nmne(uc2_network):
|
||||
@@ -15,7 +15,7 @@ def test_capture_nmne(uc2_network):
|
||||
"""
|
||||
web_server: Server = uc2_network.get_node_by_hostname("web_server") # noqa
|
||||
db_client: DatabaseClient = web_server.software_manager.software["DatabaseClient"] # noqa
|
||||
db_client.connect()
|
||||
db_client_connection: DatabaseClientConnection = db_client.get_new_connection()
|
||||
|
||||
db_server: Server = uc2_network.get_node_by_hostname("database_server") # noqa
|
||||
|
||||
@@ -39,42 +39,42 @@ def test_capture_nmne(uc2_network):
|
||||
assert db_server_nic.nmne == {}
|
||||
|
||||
# Perform a "SELECT" query
|
||||
db_client.query("SELECT")
|
||||
db_client_connection.query(sql="SELECT")
|
||||
|
||||
# Check that it does not trigger an MNE capture.
|
||||
assert web_server_nic.nmne == {}
|
||||
assert db_server_nic.nmne == {}
|
||||
|
||||
# Perform a "DELETE" query
|
||||
db_client.query("DELETE")
|
||||
db_client_connection.query(sql="DELETE")
|
||||
|
||||
# Check that the web server's outbound interface and the database server's inbound interface register the MNE
|
||||
assert web_server_nic.nmne == {"direction": {"outbound": {"keywords": {"*": 1}}}}
|
||||
assert db_server_nic.nmne == {"direction": {"inbound": {"keywords": {"*": 1}}}}
|
||||
|
||||
# Perform another "SELECT" query
|
||||
db_client.query("SELECT")
|
||||
db_client_connection.query(sql="SELECT")
|
||||
|
||||
# Check that no additional MNEs are captured
|
||||
assert web_server_nic.nmne == {"direction": {"outbound": {"keywords": {"*": 1}}}}
|
||||
assert db_server_nic.nmne == {"direction": {"inbound": {"keywords": {"*": 1}}}}
|
||||
|
||||
# Perform another "DELETE" query
|
||||
db_client.query("DELETE")
|
||||
db_client_connection.query(sql="DELETE")
|
||||
|
||||
# Check that the web server and database server interfaces register an additional MNE
|
||||
assert web_server_nic.nmne == {"direction": {"outbound": {"keywords": {"*": 2}}}}
|
||||
assert db_server_nic.nmne == {"direction": {"inbound": {"keywords": {"*": 2}}}}
|
||||
|
||||
# Perform an "ENCRYPT" query
|
||||
db_client.query("ENCRYPT")
|
||||
db_client_connection.query(sql="ENCRYPT")
|
||||
|
||||
# Check that the web server and database server interfaces register an additional MNE
|
||||
assert web_server_nic.nmne == {"direction": {"outbound": {"keywords": {"*": 3}}}}
|
||||
assert db_server_nic.nmne == {"direction": {"inbound": {"keywords": {"*": 3}}}}
|
||||
|
||||
# Perform another "SELECT" query
|
||||
db_client.query("SELECT")
|
||||
db_client_connection.query(sql="SELECT")
|
||||
|
||||
# Check that no additional MNEs are captured
|
||||
assert web_server_nic.nmne == {"direction": {"outbound": {"keywords": {"*": 3}}}}
|
||||
@@ -92,7 +92,7 @@ def test_describe_state_nmne(uc2_network):
|
||||
"""
|
||||
web_server: Server = uc2_network.get_node_by_hostname("web_server") # noqa
|
||||
db_client: DatabaseClient = web_server.software_manager.software["DatabaseClient"] # noqa
|
||||
db_client.connect()
|
||||
db_client_connection: DatabaseClientConnection = db_client.get_new_connection()
|
||||
|
||||
db_server: Server = uc2_network.get_node_by_hostname("database_server") # noqa
|
||||
|
||||
@@ -119,7 +119,7 @@ def test_describe_state_nmne(uc2_network):
|
||||
assert db_server_nic_state["nmne"] == {}
|
||||
|
||||
# Perform a "SELECT" query
|
||||
db_client.query("SELECT")
|
||||
db_client_connection.query(sql="SELECT")
|
||||
|
||||
# Check that it does not trigger an MNE capture.
|
||||
web_server_nic_state = web_server_nic.describe_state()
|
||||
@@ -129,7 +129,7 @@ def test_describe_state_nmne(uc2_network):
|
||||
assert db_server_nic_state["nmne"] == {}
|
||||
|
||||
# Perform a "DELETE" query
|
||||
db_client.query("DELETE")
|
||||
db_client_connection.query(sql="DELETE")
|
||||
|
||||
# Check that the web server's outbound interface and the database server's inbound interface register the MNE
|
||||
web_server_nic_state = web_server_nic.describe_state()
|
||||
@@ -139,7 +139,7 @@ def test_describe_state_nmne(uc2_network):
|
||||
assert db_server_nic_state["nmne"] == {"direction": {"inbound": {"keywords": {"*": 1}}}}
|
||||
|
||||
# Perform another "SELECT" query
|
||||
db_client.query("SELECT")
|
||||
db_client_connection.query(sql="SELECT")
|
||||
|
||||
# Check that no additional MNEs are captured
|
||||
web_server_nic_state = web_server_nic.describe_state()
|
||||
@@ -149,7 +149,7 @@ def test_describe_state_nmne(uc2_network):
|
||||
assert db_server_nic_state["nmne"] == {"direction": {"inbound": {"keywords": {"*": 1}}}}
|
||||
|
||||
# Perform another "DELETE" query
|
||||
db_client.query("DELETE")
|
||||
db_client_connection.query(sql="DELETE")
|
||||
|
||||
# Check that the web server and database server interfaces register an additional MNE
|
||||
web_server_nic_state = web_server_nic.describe_state()
|
||||
@@ -159,7 +159,7 @@ def test_describe_state_nmne(uc2_network):
|
||||
assert db_server_nic_state["nmne"] == {"direction": {"inbound": {"keywords": {"*": 2}}}}
|
||||
|
||||
# Perform a "ENCRYPT" query
|
||||
db_client.query("ENCRYPT")
|
||||
db_client_connection.query(sql="ENCRYPT")
|
||||
|
||||
# Check that the web server's outbound interface and the database server's inbound interface register the MNE
|
||||
web_server_nic_state = web_server_nic.describe_state()
|
||||
@@ -169,7 +169,7 @@ def test_describe_state_nmne(uc2_network):
|
||||
assert db_server_nic_state["nmne"] == {"direction": {"inbound": {"keywords": {"*": 3}}}}
|
||||
|
||||
# Perform another "SELECT" query
|
||||
db_client.query("SELECT")
|
||||
db_client_connection.query(sql="SELECT")
|
||||
|
||||
# Check that no additional MNEs are captured
|
||||
web_server_nic_state = web_server_nic.describe_state()
|
||||
@@ -179,7 +179,7 @@ def test_describe_state_nmne(uc2_network):
|
||||
assert db_server_nic_state["nmne"] == {"direction": {"inbound": {"keywords": {"*": 3}}}}
|
||||
|
||||
# Perform another "ENCRYPT"
|
||||
db_client.query("ENCRYPT")
|
||||
db_client_connection.query(sql="ENCRYPT")
|
||||
|
||||
# Check that the web server and database server interfaces register an additional MNE
|
||||
web_server_nic_state = web_server_nic.describe_state()
|
||||
@@ -206,7 +206,7 @@ def test_capture_nmne_observations(uc2_network):
|
||||
|
||||
web_server: Server = uc2_network.get_node_by_hostname("web_server")
|
||||
db_client: DatabaseClient = web_server.software_manager.software["DatabaseClient"]
|
||||
db_client.connect()
|
||||
db_client_connection: DatabaseClientConnection = db_client.get_new_connection()
|
||||
|
||||
# Set the NMNE configuration to capture DELETE/ENCRYPT queries as MNEs
|
||||
nmne_config = {
|
||||
@@ -228,7 +228,7 @@ def test_capture_nmne_observations(uc2_network):
|
||||
for i in range(0, 20):
|
||||
# Perform a "DELETE" query each iteration
|
||||
for j in range(i):
|
||||
db_client.query("DELETE")
|
||||
db_client_connection.query(sql="DELETE")
|
||||
|
||||
# Observe the current state of NMNEs from the NICs of both the database and web servers
|
||||
state = sim.describe_state()
|
||||
@@ -253,7 +253,7 @@ def test_capture_nmne_observations(uc2_network):
|
||||
for i in range(0, 20):
|
||||
# Perform a "ENCRYPT" query each iteration
|
||||
for j in range(i):
|
||||
db_client.query("ENCRYPT")
|
||||
db_client_connection.query(sql="ENCRYPT")
|
||||
|
||||
# Observe the current state of NMNEs from the NICs of both the database and web servers
|
||||
state = sim.describe_state()
|
||||
|
||||
@@ -10,7 +10,7 @@ from primaite.simulator.network.hardware.nodes.host.server import Server
|
||||
from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.applications.application import ApplicationOperatingState
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient, DatabaseClientConnection
|
||||
from primaite.simulator.system.applications.red_applications.data_manipulation_bot import (
|
||||
DataManipulationAttackStage,
|
||||
DataManipulationBot,
|
||||
@@ -141,8 +141,10 @@ def test_data_manipulation_disrupts_green_agent_connection(data_manipulation_db_
|
||||
server: Server = network.get_node_by_hostname("server_1")
|
||||
db_server_service: DatabaseService = server.software_manager.software.get("DatabaseService")
|
||||
|
||||
green_db_connection: DatabaseClientConnection = green_db_client.get_new_connection()
|
||||
|
||||
assert db_server_service.db_file.health_status is FileSystemItemHealthStatus.GOOD
|
||||
assert green_db_client.query("SELECT")
|
||||
assert green_db_connection.query("SELECT")
|
||||
assert green_db_client.last_query_response.get("status_code") == 200
|
||||
|
||||
data_manipulation_bot.port_scan_p_of_success = 1
|
||||
@@ -151,5 +153,5 @@ def test_data_manipulation_disrupts_green_agent_connection(data_manipulation_db_
|
||||
data_manipulation_bot.attack()
|
||||
|
||||
assert db_server_service.db_file.health_status is FileSystemItemHealthStatus.COMPROMISED
|
||||
assert green_db_client.query("SELECT") is False
|
||||
assert green_db_connection.query("SELECT") is False
|
||||
assert green_db_client.last_query_response.get("status_code") != 200
|
||||
|
||||
@@ -10,7 +10,7 @@ from primaite.simulator.network.hardware.nodes.host.server import Server
|
||||
from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.applications.application import ApplicationOperatingState
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient, DatabaseClientConnection
|
||||
from primaite.simulator.system.applications.red_applications.ransomware_script import (
|
||||
RansomwareAttackStage,
|
||||
RansomwareScript,
|
||||
@@ -144,12 +144,13 @@ def test_ransomware_disrupts_green_agent_connection(ransomware_script_db_server_
|
||||
|
||||
client_2: Computer = network.get_node_by_hostname("client_2")
|
||||
green_db_client: DatabaseClient = client_2.software_manager.software.get("DatabaseClient")
|
||||
green_db_client_connection: DatabaseClientConnection = green_db_client.get_new_connection()
|
||||
|
||||
server: Server = network.get_node_by_hostname("server_1")
|
||||
db_server_service: DatabaseService = server.software_manager.software.get("DatabaseService")
|
||||
|
||||
assert db_server_service.db_file.health_status is FileSystemItemHealthStatus.GOOD
|
||||
assert green_db_client.query("SELECT")
|
||||
assert green_db_client_connection.query("SELECT")
|
||||
assert green_db_client.last_query_response.get("status_code") == 200
|
||||
|
||||
ransomware_script_application.target_scan_p_of_success = 1
|
||||
@@ -159,5 +160,5 @@ def test_ransomware_disrupts_green_agent_connection(ransomware_script_db_server_
|
||||
ransomware_script_application.attack()
|
||||
|
||||
assert db_server_service.db_file.health_status is FileSystemItemHealthStatus.CORRUPT
|
||||
assert green_db_client.query("SELECT") is True
|
||||
assert green_db_client_connection.query("SELECT") is True
|
||||
assert green_db_client.last_query_response.get("status_code") == 200
|
||||
|
||||
@@ -8,7 +8,8 @@ from primaite.simulator.network.container import Network
|
||||
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
from primaite.simulator.network.hardware.nodes.host.server import Server
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient
|
||||
from primaite.simulator.network.hardware.nodes.network.switch import Switch
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient, DatabaseClientConnection
|
||||
from primaite.simulator.system.services.database.database_service import DatabaseService
|
||||
from primaite.simulator.system.services.ftp.ftp_server import FTPServer
|
||||
from primaite.simulator.system.services.service import ServiceOperatingState
|
||||
@@ -56,11 +57,12 @@ def test_database_client_server_connection(peer_to_peer):
|
||||
db_service: DatabaseService = node_b.software_manager.software["DatabaseService"]
|
||||
|
||||
db_client.connect()
|
||||
assert len(db_client.connections) == 1
|
||||
|
||||
assert len(db_client.client_connections) == 1
|
||||
assert len(db_service.connections) == 1
|
||||
|
||||
db_client.disconnect()
|
||||
assert len(db_client.connections) == 0
|
||||
assert len(db_client.client_connections) == 0
|
||||
assert len(db_service.connections) == 0
|
||||
|
||||
|
||||
@@ -73,7 +75,7 @@ def test_database_client_server_correct_password(peer_to_peer_secure_db):
|
||||
|
||||
db_client.configure(server_ip_address=IPv4Address("192.168.0.11"), server_password="12345")
|
||||
db_client.connect()
|
||||
assert len(db_client.connections) == 1
|
||||
assert len(db_client.client_connections) == 1
|
||||
assert len(db_service.connections) == 1
|
||||
|
||||
|
||||
@@ -95,14 +97,24 @@ def test_database_client_server_incorrect_password(peer_to_peer_secure_db):
|
||||
assert len(db_service.connections) == 0
|
||||
|
||||
|
||||
def test_database_client_query(uc2_network):
|
||||
def test_database_client_native_connection_query(uc2_network):
|
||||
"""Tests DB query across the network returns HTTP status 200 and date."""
|
||||
web_server: Server = uc2_network.get_node_by_hostname("web_server")
|
||||
db_client: DatabaseClient = web_server.software_manager.software["DatabaseClient"]
|
||||
db_client.connect()
|
||||
|
||||
assert db_client.query("SELECT")
|
||||
assert db_client.query("INSERT")
|
||||
assert db_client.query(sql="SELECT")
|
||||
assert db_client.query(sql="INSERT")
|
||||
|
||||
|
||||
def test_database_client_connection_query(uc2_network):
|
||||
"""Tests DB query across the network returns HTTP status 200 and date."""
|
||||
web_server: Server = uc2_network.get_node_by_hostname("web_server")
|
||||
db_client: DatabaseClient = web_server.software_manager.software["DatabaseClient"]
|
||||
|
||||
db_connection: DatabaseClientConnection = db_client.get_new_connection()
|
||||
|
||||
assert db_connection.query(sql="SELECT")
|
||||
assert db_connection.query(sql="INSERT")
|
||||
|
||||
|
||||
def test_create_database_backup(uc2_network):
|
||||
@@ -172,7 +184,6 @@ def test_restore_backup_after_deleting_file_without_updating_scan(uc2_network):
|
||||
db_server: Server = uc2_network.get_node_by_hostname("database_server")
|
||||
db_service: DatabaseService = db_server.software_manager.software["DatabaseService"]
|
||||
|
||||
# create a back up
|
||||
assert db_service.backup_database() is True
|
||||
|
||||
db_service.db_file.corrupt() # corrupt the db
|
||||
@@ -211,10 +222,13 @@ def test_database_client_cannot_query_offline_database_server(uc2_network):
|
||||
|
||||
web_server: Server = uc2_network.get_node_by_hostname("web_server")
|
||||
db_client: DatabaseClient = web_server.software_manager.software.get("DatabaseClient")
|
||||
assert len(db_client.connections)
|
||||
assert len(db_client.client_connections)
|
||||
|
||||
assert db_client.query("SELECT") is True
|
||||
assert db_client.query("INSERT") is True
|
||||
# Establish a new connection to the DatabaseService
|
||||
db_connection: DatabaseClientConnection = db_client.get_new_connection()
|
||||
|
||||
assert db_connection.query("SELECT") is True
|
||||
assert db_connection.query("INSERT") is True
|
||||
db_server.power_off()
|
||||
|
||||
for i in range(db_server.shut_down_duration + 1):
|
||||
@@ -223,5 +237,121 @@ def test_database_client_cannot_query_offline_database_server(uc2_network):
|
||||
assert db_server.operating_state is NodeOperatingState.OFF
|
||||
assert db_service.operating_state is ServiceOperatingState.STOPPED
|
||||
|
||||
assert db_client.query("SELECT") is False
|
||||
assert db_client.query("INSERT") is False
|
||||
assert db_connection.query("SELECT") is False
|
||||
assert db_connection.query("INSERT") is False
|
||||
|
||||
|
||||
def test_database_client_uninstall_terminates_connections(peer_to_peer):
|
||||
node_a, node_b = peer_to_peer
|
||||
|
||||
db_client: DatabaseClient = node_a.software_manager.software["DatabaseClient"]
|
||||
db_service: DatabaseService = node_b.software_manager.software["DatabaseService"] # noqa
|
||||
|
||||
db_connection: DatabaseClientConnection = db_client.get_new_connection()
|
||||
|
||||
# Check that all connection counters are correct and that the client connection can query the database
|
||||
assert len(db_service.connections) == 1
|
||||
|
||||
assert len(db_client.client_connections) == 1
|
||||
|
||||
assert db_connection.is_active
|
||||
|
||||
assert db_connection.query("SELECT")
|
||||
|
||||
# Perform the DatabaseClient uninstall
|
||||
node_a.software_manager.uninstall("DatabaseClient")
|
||||
|
||||
# Check that all connection counters are updated accordingly and client connection can no longer query the database
|
||||
assert len(db_service.connections) == 0
|
||||
|
||||
assert len(db_client.client_connections) == 0
|
||||
|
||||
assert not db_connection.query("SELECT")
|
||||
|
||||
assert not db_connection.is_active
|
||||
|
||||
|
||||
def test_database_service_can_terminate_connection(peer_to_peer):
|
||||
node_a, node_b = peer_to_peer
|
||||
|
||||
db_client: DatabaseClient = node_a.software_manager.software["DatabaseClient"]
|
||||
db_service: DatabaseService = node_b.software_manager.software["DatabaseService"] # noqa
|
||||
|
||||
db_connection: DatabaseClientConnection = db_client.get_new_connection()
|
||||
|
||||
# Check that all connection counters are correct and that the client connection can query the database
|
||||
assert len(db_service.connections) == 1
|
||||
|
||||
assert len(db_client.client_connections) == 1
|
||||
|
||||
assert db_connection.is_active
|
||||
|
||||
assert db_connection.query("SELECT")
|
||||
|
||||
# Perform the server-led connection termination
|
||||
connection_id = next(iter(db_service.connections.keys()))
|
||||
db_service.terminate_connection(connection_id)
|
||||
|
||||
# Check that all connection counters are updated accordingly and client connection can no longer query the database
|
||||
assert len(db_service.connections) == 0
|
||||
|
||||
assert len(db_client.client_connections) == 0
|
||||
|
||||
assert not db_connection.query("SELECT")
|
||||
|
||||
assert not db_connection.is_active
|
||||
|
||||
|
||||
def test_client_connection_terminate_does_not_terminate_another_clients_connection():
|
||||
network = Network()
|
||||
|
||||
db_server = Server(
|
||||
hostname="db_client", ip_address="192.168.0.11", subnet_mask="255.255.255.0", start_up_duration=0
|
||||
)
|
||||
db_server.power_on()
|
||||
|
||||
db_server.software_manager.install(DatabaseService)
|
||||
db_service: DatabaseService = db_server.software_manager.software["DatabaseService"] # noqa
|
||||
db_service.start()
|
||||
|
||||
client_a = Computer(
|
||||
hostname="client_a", ip_address="192.168.0.12", subnet_mask="255.255.255.0", start_up_duration=0
|
||||
)
|
||||
client_a.power_on()
|
||||
|
||||
client_a.software_manager.install(DatabaseClient)
|
||||
client_a.software_manager.software["DatabaseClient"].configure(server_ip_address=IPv4Address("192.168.0.11"))
|
||||
client_a.software_manager.software["DatabaseClient"].run()
|
||||
|
||||
client_b = Computer(
|
||||
hostname="client_b", ip_address="192.168.0.13", subnet_mask="255.255.255.0", start_up_duration=0
|
||||
)
|
||||
client_b.power_on()
|
||||
|
||||
client_b.software_manager.install(DatabaseClient)
|
||||
client_b.software_manager.software["DatabaseClient"].configure(server_ip_address=IPv4Address("192.168.0.11"))
|
||||
client_b.software_manager.software["DatabaseClient"].run()
|
||||
|
||||
switch = Switch(hostname="switch", start_up_duration=0, num_ports=3)
|
||||
switch.power_on()
|
||||
|
||||
network.connect(endpoint_a=switch.network_interface[1], endpoint_b=db_server.network_interface[1])
|
||||
network.connect(endpoint_a=switch.network_interface[2], endpoint_b=client_a.network_interface[1])
|
||||
network.connect(endpoint_a=switch.network_interface[3], endpoint_b=client_b.network_interface[1])
|
||||
|
||||
db_client_a: DatabaseClient = client_a.software_manager.software["DatabaseClient"] # noqa
|
||||
db_connection_a = db_client_a.get_new_connection()
|
||||
|
||||
assert db_connection_a.query("SELECT")
|
||||
assert len(db_service.connections) == 1
|
||||
|
||||
db_client_b: DatabaseClient = client_b.software_manager.software["DatabaseClient"] # noqa
|
||||
db_connection_b = db_client_b.get_new_connection()
|
||||
|
||||
assert db_connection_b.query("SELECT")
|
||||
assert len(db_service.connections) == 2
|
||||
|
||||
db_connection_a.disconnect()
|
||||
|
||||
assert db_connection_b.query("SELECT")
|
||||
assert len(db_service.connections) == 1
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import pytest
|
||||
|
||||
from primaite.interface.request import RequestResponse
|
||||
from primaite.simulator.network.container import Network
|
||||
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
|
||||
from primaite.simulator.network.hardware.nodes.host.host_node import HostNode
|
||||
from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router
|
||||
@@ -97,7 +98,7 @@ def test_request_fails_if_node_off(example_network, node_request):
|
||||
class TestDataManipulationGreenRequests:
|
||||
def test_node_off(self, uc2_network):
|
||||
"""Test that green requests succeed when the node is on and fail if the node is off."""
|
||||
net = uc2_network
|
||||
net: Network = uc2_network
|
||||
|
||||
client_1_browser_execute = net.apply_request(["node", "client_1", "application", "WebBrowser", "execute"])
|
||||
client_1_db_client_execute = net.apply_request(["node", "client_1", "application", "DatabaseClient", "execute"])
|
||||
@@ -131,7 +132,7 @@ class TestDataManipulationGreenRequests:
|
||||
|
||||
def test_acl_block(self, uc2_network):
|
||||
"""Test that green requests succeed when not blocked by ACLs but fail when blocked."""
|
||||
net = uc2_network
|
||||
net: Network = uc2_network
|
||||
|
||||
router: Router = net.get_node_by_hostname("router_1")
|
||||
client_1: HostNode = net.get_node_by_hostname("client_1")
|
||||
|
||||
0
tests/unit_tests/_primaite/_session/__init__.py
Normal file
0
tests/unit_tests/_primaite/_session/__init__.py
Normal file
50
tests/unit_tests/_primaite/_session/test_episode_schedule.py
Normal file
50
tests/unit_tests/_primaite/_session/test_episode_schedule.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from primaite.session.episode_schedule import ConstantEpisodeScheduler, EpisodeListScheduler
|
||||
|
||||
|
||||
def test_episode_list_scheduler():
|
||||
# Initialize an instance of EpisodeListScheduler
|
||||
|
||||
# Define a schedule and episode data for testing
|
||||
schedule = {0: ["episode1"], 1: ["episode2"]}
|
||||
episode_data = {"episode1": "data1: 1", "episode2": "data2: 2"}
|
||||
base_scenario = """agents: []"""
|
||||
|
||||
scheduler = EpisodeListScheduler(schedule=schedule, episode_data=episode_data, base_scenario=base_scenario)
|
||||
# Test when episode number is within the schedule
|
||||
result = scheduler(0)
|
||||
assert isinstance(result, dict)
|
||||
assert yaml.safe_load("data1: 1\nagents: []") == result
|
||||
|
||||
# Test next episode
|
||||
result = scheduler(1)
|
||||
assert isinstance(result, dict)
|
||||
assert yaml.safe_load("data2: 2\nagents: []") == result
|
||||
|
||||
# Test when episode number exceeds the schedule
|
||||
result = scheduler(2)
|
||||
assert isinstance(result, dict)
|
||||
assert yaml.safe_load("data1: 1\nagents: []") == result
|
||||
assert scheduler._exceeded_episode_list
|
||||
|
||||
# Test when episode number is a sequence
|
||||
scheduler.schedule = {0: ["episode1", "episode2"]}
|
||||
result = scheduler(0)
|
||||
assert isinstance(result, dict)
|
||||
assert yaml.safe_load("data1: 1\ndata2: 2\nagents: []") == result
|
||||
|
||||
|
||||
def test_constant_episode_scheduler():
|
||||
# Initialize an instance of ConstantEpisodeScheduler
|
||||
config = {"key": "value"}
|
||||
scheduler = ConstantEpisodeScheduler(config=config)
|
||||
|
||||
result = scheduler(0)
|
||||
assert isinstance(result, dict)
|
||||
assert {"key": "value"} == result
|
||||
|
||||
result = scheduler(1)
|
||||
assert isinstance(result, dict)
|
||||
assert {"key": "value"} == result
|
||||
@@ -70,7 +70,7 @@ def test_dm_bot_perform_data_manipulation_success(dm_bot):
|
||||
dm_bot._perform_data_manipulation(p_of_success=1.0)
|
||||
|
||||
assert dm_bot.attack_stage in (DataManipulationAttackStage.SUCCEEDED, DataManipulationAttackStage.FAILED)
|
||||
assert len(dm_bot._host_db_client.connections)
|
||||
assert len(dm_bot._host_db_client.client_connections)
|
||||
|
||||
|
||||
def test_dm_bot_fails_without_db_client(dm_client):
|
||||
|
||||
@@ -56,7 +56,11 @@ def test_connect_to_database_fails_on_reattempt(database_client_on_computer):
|
||||
database_client, computer = database_client_on_computer
|
||||
|
||||
database_client.connected = False
|
||||
assert database_client._connect(server_ip_address=IPv4Address("192.168.0.1"), is_reattempt=True) is False
|
||||
|
||||
database_connection = database_client._connect(
|
||||
server_ip_address=IPv4Address("192.168.0.1"), connection_request_id="", is_reattempt=True
|
||||
)
|
||||
assert database_connection is None
|
||||
|
||||
|
||||
def test_disconnect_when_client_is_closed(database_client_on_computer):
|
||||
@@ -79,21 +83,20 @@ def test_disconnect(database_client_on_computer):
|
||||
"""Database client should remove the connection."""
|
||||
database_client, computer = database_client_on_computer
|
||||
|
||||
assert not database_client.connected
|
||||
assert database_client.connected is False
|
||||
|
||||
database_client.connect()
|
||||
|
||||
assert database_client.connected
|
||||
assert database_client.connected is True
|
||||
|
||||
database_client.disconnect()
|
||||
|
||||
assert not database_client.connected
|
||||
assert database_client.connected is False
|
||||
|
||||
|
||||
def test_query_when_client_is_closed(database_client_on_computer):
|
||||
"""Database client should return False when it is not running."""
|
||||
database_client, computer = database_client_on_computer
|
||||
|
||||
database_client.close()
|
||||
assert database_client.operating_state is ApplicationOperatingState.CLOSED
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.simulator.system.services.service import ServiceOperatingState
|
||||
from primaite.simulator.system.software import SoftwareHealthState
|
||||
|
||||
@@ -179,7 +181,8 @@ def test_overwhelm_service(service):
|
||||
assert service.health_state_actual is SoftwareHealthState.OVERWHELMED
|
||||
|
||||
|
||||
def test_create_and_remove_connections(service):
|
||||
@pytest.mark.xfail(reason="Fails as it's now too simple. Needs to be be refactored so that uses a service on a node.")
|
||||
def test_create_and_terminate_connections(service):
|
||||
service.start()
|
||||
uuid = str(uuid4())
|
||||
|
||||
@@ -187,6 +190,6 @@ def test_create_and_remove_connections(service):
|
||||
assert len(service.connections) == 1
|
||||
assert service.health_state_actual is SoftwareHealthState.GOOD
|
||||
|
||||
assert service.remove_connection(connection_id=uuid) # should be true
|
||||
assert service.terminate_connection(connection_id=uuid) # should be true
|
||||
assert len(service.connections) == 0
|
||||
assert service.health_state_actual is SoftwareHealthState.GOOD
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.simulator import LogLevel, SIM_OUTPUT
|
||||
from primaite.simulator.system.core.sys_log import SysLog
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def syslog() -> SysLog:
|
||||
return SysLog(hostname="test")
|
||||
|
||||
|
||||
def test_debug_sys_log_level(syslog, capsys):
|
||||
"""Test that the debug log level logs debug syslogs and above."""
|
||||
SIM_OUTPUT.sys_log_level = LogLevel.DEBUG
|
||||
SIM_OUTPUT.write_sys_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
syslog.debug(test_string)
|
||||
syslog.info(test_string)
|
||||
syslog.warning(test_string)
|
||||
syslog.error(test_string)
|
||||
syslog.critical(test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" in captured
|
||||
assert "INFO" in captured
|
||||
assert "WARNING" in captured
|
||||
assert "ERROR" in captured
|
||||
assert "CRITICAL" in captured
|
||||
|
||||
|
||||
def test_info_sys_log_level(syslog, capsys):
|
||||
"""Test that the debug log level logs debug syslogs and above."""
|
||||
SIM_OUTPUT.sys_log_level = LogLevel.INFO
|
||||
SIM_OUTPUT.write_sys_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
syslog.debug(test_string)
|
||||
syslog.info(test_string)
|
||||
syslog.warning(test_string)
|
||||
syslog.error(test_string)
|
||||
syslog.critical(test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" not in captured
|
||||
assert "INFO" in captured
|
||||
assert "WARNING" in captured
|
||||
assert "ERROR" in captured
|
||||
assert "CRITICAL" in captured
|
||||
|
||||
|
||||
def test_warning_sys_log_level(syslog, capsys):
|
||||
"""Test that the debug log level logs debug syslogs and above."""
|
||||
SIM_OUTPUT.sys_log_level = LogLevel.WARNING
|
||||
SIM_OUTPUT.write_sys_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
syslog.debug(test_string)
|
||||
syslog.info(test_string)
|
||||
syslog.warning(test_string)
|
||||
syslog.error(test_string)
|
||||
syslog.critical(test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" not in captured
|
||||
assert "INFO" not in captured
|
||||
assert "WARNING" in captured
|
||||
assert "ERROR" in captured
|
||||
assert "CRITICAL" in captured
|
||||
|
||||
|
||||
def test_error_sys_log_level(syslog, capsys):
|
||||
"""Test that the debug log level logs debug syslogs and above."""
|
||||
SIM_OUTPUT.sys_log_level = LogLevel.ERROR
|
||||
SIM_OUTPUT.write_sys_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
syslog.debug(test_string)
|
||||
syslog.info(test_string)
|
||||
syslog.warning(test_string)
|
||||
syslog.error(test_string)
|
||||
syslog.critical(test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" not in captured
|
||||
assert "INFO" not in captured
|
||||
assert "WARNING" not in captured
|
||||
assert "ERROR" in captured
|
||||
assert "CRITICAL" in captured
|
||||
|
||||
|
||||
def test_critical_sys_log_level(syslog, capsys):
|
||||
"""Test that the debug log level logs debug syslogs and above."""
|
||||
SIM_OUTPUT.sys_log_level = LogLevel.CRITICAL
|
||||
SIM_OUTPUT.write_sys_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
syslog.debug(test_string)
|
||||
syslog.info(test_string)
|
||||
syslog.warning(test_string)
|
||||
syslog.error(test_string)
|
||||
syslog.critical(test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" not in captured
|
||||
assert "INFO" not in captured
|
||||
assert "WARNING" not in captured
|
||||
assert "ERROR" not in captured
|
||||
assert "CRITICAL" in captured
|
||||
Reference in New Issue
Block a user