Path: blob/main/cyberbattle/simulation/model_test.py
597 views
# Copyright (c) Microsoft Corporation.1# Licensed under the MIT License.23"""4Unit tests for model.py.56Note that model.py mainly provides the data modelling for the simulation,7that is naked data types without members. There is therefore not much8relevant unit testing that can be implemented at this stage.9Once we add operations to generate and modify environments there10will be more room for unit-testing.1112"""13# pylint: disable=missing-function-docstring1415from cyberbattle.simulation.model import AdminEscalation, Identifiers, SystemEscalation16import yaml17from datetime import datetime1819import networkx as nx2021from . import model2223ADMINTAG = AdminEscalation().tag24SYSTEMTAG = SystemEscalation().tag2526vulnerabilities = {27"UACME61": model.VulnerabilityInfo(28description="UACME UAC bypass #61",29type=model.VulnerabilityType.LOCAL,30URL="https://github.com/hfiref0x/UACME",31precondition=model.Precondition(f"Windows&Win10&(~({ADMINTAG}|{SYSTEMTAG}))"),32outcome=model.AdminEscalation(),33rates=model.Rates(0, 0.2, 1.0),34)35}3637ENV_IDENTIFIERS = Identifiers(properties=[], ports=[], local_vulnerabilities=["UACME61"], remote_vulnerabilities=[])383940# Verify that there is a unique node injected with the41# attacker in a randomly generated graph42def test_single_infected_node_initially() -> None:43# create a random environment44graph = nx.cubical_graph()45graph = model.assign_random_labels(graph)46env = model.Environment(network=graph, vulnerability_library=dict([]), identifiers=ENV_IDENTIFIERS)47count = sum(1 for i in graph.nodes if env.get_node(i).agent_installed)48assert count == 149return505152# ensures that an environment can successfully be serialized as yaml53def test_environment_is_serializable() -> None:54# create a random environment55env = model.Environment(56network=model.assign_random_labels(nx.cubical_graph()),57version=model.VERSION_TAG,58vulnerability_library=dict([]),59identifiers=ENV_IDENTIFIERS,60creationTime=datetime.utcnow(),61lastModified=datetime.utcnow(),62)63# Dump the environment as Yaml64_ = yaml.dump(env)65assert True666768# Test random graph get_node_information69def test_create_random_environment() -> None:70graph = nx.cubical_graph()7172graph = model.assign_random_labels(graph)7374env = model.Environment(network=graph, vulnerability_library=vulnerabilities, identifiers=ENV_IDENTIFIERS)75assert env76pass777879def check_reserializing(object_to_serialize: object) -> None:80"""Helper function to check that deserializing and serializing are inverse of each other"""81serialized = yaml.dump(object_to_serialize)82# print('Serialized: ' + serialized)83deserialized = yaml.load(serialized, yaml.Loader)84re_serialized = yaml.dump(deserialized)85assert serialized == re_serialized868788def test_yaml_serialization_networkx() -> None:89"""Test Yaml serialization and deserialization for type Environment"""90model.setup_yaml_serializer()91check_reserializing(model.assign_random_labels(nx.cubical_graph()))929394def test_yaml_serialization_environment() -> None:95"""Test Yaml serialization and deserialization for type Environment9697Note: if `model.Environment` is declared as a NamedTuple instead of a @dataclass98then this test breaks with networkx>=2.8.1 (but works with 2.8.0)99due to the new networkx field `edges._graph` self referencing the graph.100"""101network = model.assign_random_labels(nx.cubical_graph())102env = model.Environment(network=network, vulnerability_library=vulnerabilities, identifiers=model.infer_constants_from_network(network, vulnerabilities))103104model.setup_yaml_serializer()105106serialized = yaml.dump(env)107assert len(serialized) > 100108109check_reserializing(env)110111112def test_yaml_serialization_precondition() -> None:113"""Test Yaml serialization and deserialization for type Precondition"""114model.setup_yaml_serializer()115116precondition = model.Precondition(f"Windows&Win10&(~({ADMINTAG}|{SYSTEMTAG}))")117check_reserializing(precondition)118119deserialized = yaml.safe_load(yaml.dump(precondition))120assert precondition.expression == deserialized.expression121122123def test_yaml_serialization_vulnerabilitytype() -> None:124"""Test Yaml serialization and deserialization for type VulnerabilityType"""125model.setup_yaml_serializer()126127object_to_serialize = model.VulnerabilityType.LOCAL128check_reserializing(object_to_serialize)129130131