Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/CyberBattleSim
Path: blob/main/cyberbattle/simulation/model_test.py
597 views
1
# Copyright (c) Microsoft Corporation.
2
# Licensed under the MIT License.
3
4
"""
5
Unit tests for model.py.
6
7
Note that model.py mainly provides the data modelling for the simulation,
8
that is naked data types without members. There is therefore not much
9
relevant unit testing that can be implemented at this stage.
10
Once we add operations to generate and modify environments there
11
will be more room for unit-testing.
12
13
"""
14
# pylint: disable=missing-function-docstring
15
16
from cyberbattle.simulation.model import AdminEscalation, Identifiers, SystemEscalation
17
import yaml
18
from datetime import datetime
19
20
import networkx as nx
21
22
from . import model
23
24
ADMINTAG = AdminEscalation().tag
25
SYSTEMTAG = SystemEscalation().tag
26
27
vulnerabilities = {
28
"UACME61": model.VulnerabilityInfo(
29
description="UACME UAC bypass #61",
30
type=model.VulnerabilityType.LOCAL,
31
URL="https://github.com/hfiref0x/UACME",
32
precondition=model.Precondition(f"Windows&Win10&(~({ADMINTAG}|{SYSTEMTAG}))"),
33
outcome=model.AdminEscalation(),
34
rates=model.Rates(0, 0.2, 1.0),
35
)
36
}
37
38
ENV_IDENTIFIERS = Identifiers(properties=[], ports=[], local_vulnerabilities=["UACME61"], remote_vulnerabilities=[])
39
40
41
# Verify that there is a unique node injected with the
42
# attacker in a randomly generated graph
43
def test_single_infected_node_initially() -> None:
44
# create a random environment
45
graph = nx.cubical_graph()
46
graph = model.assign_random_labels(graph)
47
env = model.Environment(network=graph, vulnerability_library=dict([]), identifiers=ENV_IDENTIFIERS)
48
count = sum(1 for i in graph.nodes if env.get_node(i).agent_installed)
49
assert count == 1
50
return
51
52
53
# ensures that an environment can successfully be serialized as yaml
54
def test_environment_is_serializable() -> None:
55
# create a random environment
56
env = model.Environment(
57
network=model.assign_random_labels(nx.cubical_graph()),
58
version=model.VERSION_TAG,
59
vulnerability_library=dict([]),
60
identifiers=ENV_IDENTIFIERS,
61
creationTime=datetime.utcnow(),
62
lastModified=datetime.utcnow(),
63
)
64
# Dump the environment as Yaml
65
_ = yaml.dump(env)
66
assert True
67
68
69
# Test random graph get_node_information
70
def test_create_random_environment() -> None:
71
graph = nx.cubical_graph()
72
73
graph = model.assign_random_labels(graph)
74
75
env = model.Environment(network=graph, vulnerability_library=vulnerabilities, identifiers=ENV_IDENTIFIERS)
76
assert env
77
pass
78
79
80
def check_reserializing(object_to_serialize: object) -> None:
81
"""Helper function to check that deserializing and serializing are inverse of each other"""
82
serialized = yaml.dump(object_to_serialize)
83
# print('Serialized: ' + serialized)
84
deserialized = yaml.load(serialized, yaml.Loader)
85
re_serialized = yaml.dump(deserialized)
86
assert serialized == re_serialized
87
88
89
def test_yaml_serialization_networkx() -> None:
90
"""Test Yaml serialization and deserialization for type Environment"""
91
model.setup_yaml_serializer()
92
check_reserializing(model.assign_random_labels(nx.cubical_graph()))
93
94
95
def test_yaml_serialization_environment() -> None:
96
"""Test Yaml serialization and deserialization for type Environment
97
98
Note: if `model.Environment` is declared as a NamedTuple instead of a @dataclass
99
then this test breaks with networkx>=2.8.1 (but works with 2.8.0)
100
due to the new networkx field `edges._graph` self referencing the graph.
101
"""
102
network = model.assign_random_labels(nx.cubical_graph())
103
env = model.Environment(network=network, vulnerability_library=vulnerabilities, identifiers=model.infer_constants_from_network(network, vulnerabilities))
104
105
model.setup_yaml_serializer()
106
107
serialized = yaml.dump(env)
108
assert len(serialized) > 100
109
110
check_reserializing(env)
111
112
113
def test_yaml_serialization_precondition() -> None:
114
"""Test Yaml serialization and deserialization for type Precondition"""
115
model.setup_yaml_serializer()
116
117
precondition = model.Precondition(f"Windows&Win10&(~({ADMINTAG}|{SYSTEMTAG}))")
118
check_reserializing(precondition)
119
120
deserialized = yaml.safe_load(yaml.dump(precondition))
121
assert precondition.expression == deserialized.expression
122
123
124
def test_yaml_serialization_vulnerabilitytype() -> None:
125
"""Test Yaml serialization and deserialization for type VulnerabilityType"""
126
model.setup_yaml_serializer()
127
128
object_to_serialize = model.VulnerabilityType.LOCAL
129
check_reserializing(object_to_serialize)
130
131