@@ -90,8 +90,6 @@ def __init__(self, lang_graph, model: Optional[Model] = None):
90
90
91
91
self .model = model
92
92
self .lang_graph = lang_graph
93
- self .next_node_id = 0
94
- self .next_attacker_id = 0
95
93
if self .model is not None :
96
94
self ._generate_graph ()
97
95
@@ -154,10 +152,6 @@ def __deepcopy__(self, memo):
154
152
copied_attackgraph ._full_name_to_node = \
155
153
copy .deepcopy (self ._full_name_to_node , memo )
156
154
157
- # Copy counters
158
- copied_attackgraph .next_node_id = self .next_node_id
159
- copied_attackgraph .next_attacker_id = self .next_attacker_id
160
-
161
155
return copied_attackgraph
162
156
163
157
def save_to_file (self , filename : str ) -> None :
@@ -201,15 +195,17 @@ def _from_dict(
201
195
node_dict ['lang_graph_attack_step' ])
202
196
lg_attack_step = lang_graph .assets [lg_asset_name ].\
203
197
attack_steps [lg_attack_step_name ]
204
- ag_node = attack_graph . add_node (
198
+ ag_node = AttackGraphNode (
205
199
lg_attack_step = lg_attack_step ,
206
- node_id = node_dict ['id' ],
207
200
model_asset = node_asset ,
201
+ node_id = node_dict ['id' ],
208
202
defense_status = node_dict .get ('defense_status' , None ),
209
203
existence_status = node_dict .get ('existence_status' , None )
210
204
)
211
205
ag_node .tags = set (node_dict .get ('tags' , []))
212
206
ag_node .extras = node_dict .get ('extras' , {})
207
+ attack_graph .nodes [ag_node .id ] = ag_node
208
+ attack_graph ._full_name_to_node [ag_node .full_name ] = ag_node
213
209
214
210
if node_asset :
215
211
# Add AttackGraphNode to attack_step_nodes of asset
@@ -249,20 +245,14 @@ def _from_dict(
249
245
_ag_node .parents .add (parent )
250
246
251
247
for attacker in serialized_attackers .values ():
252
- ag_attacker = Attacker (
253
- name = attacker ['name' ],
254
- entry_points = set (),
255
- reached_attack_steps = set ()
256
- )
257
248
attack_graph .add_attacker (
258
- attacker = ag_attacker ,
259
- attacker_id = int (attacker ['id' ]),
249
+ attacker = Attacker (name = attacker ['name' ]),
260
250
entry_points = [
261
251
int (node_id ) # Convert to int since they can be strings
262
252
for node_id in attacker ['entry_points' ].keys ()
263
253
],
264
- reached_attack_steps = [
265
- int (node_id ) # Convert to int since they can be strings
254
+ reached_attack_steps = [
255
+ int (node_id ) # Convert to int since they can be strings
266
256
for node_id in attacker ['reached_attack_steps' ].keys ()
267
257
]
268
258
)
@@ -323,18 +313,15 @@ def attach_attackers(self) -> None:
323
313
'Attach attackers from "%s" model to the graph.' , self .model .name
324
314
)
325
315
316
+ Attacker .reset_ids ()
326
317
for attacker_info in self .model .attackers :
327
318
328
319
if not attacker_info .name :
329
320
msg = "Can not attach attacker without name"
330
321
logger .error (msg )
331
322
raise AttackGraphException (msg )
332
323
333
- attacker = Attacker (
334
- name = attacker_info .name ,
335
- entry_points = set (),
336
- reached_attack_steps = set ()
337
- )
324
+ attacker = Attacker (attacker_info .name )
338
325
self .add_attacker (attacker )
339
326
340
327
for (asset , attack_steps ) in attacker_info .entry_points :
@@ -520,6 +507,7 @@ def _generate_graph(self) -> None:
520
507
Generate the attack graph based on the original model instance and the
521
508
MAL language specification provided at initialization.
522
509
"""
510
+ AttackGraphNode .reset_ids ()
523
511
524
512
if not self .model :
525
513
msg = "Can not generate AttackGraph without model"
@@ -580,12 +568,14 @@ def _generate_graph(self) -> None:
580
568
case _:
581
569
pass
582
570
583
- ag_node = self . add_node (
571
+ ag_node = AttackGraphNode (
584
572
lg_attack_step = attack_step ,
585
573
model_asset = asset ,
586
574
defense_status = defense_status ,
587
575
existence_status = existence_status
588
576
)
577
+ self .nodes [ag_node .id ] = ag_node
578
+ self ._full_name_to_node [ag_node .full_name ] = ag_node
589
579
attack_step_nodes .append (ag_node )
590
580
591
581
asset .attack_step_nodes = attack_step_nodes
@@ -669,64 +659,6 @@ def regenerate_graph(self) -> None:
669
659
self .attackers = {}
670
660
self ._generate_graph ()
671
661
672
- def add_node (
673
- self ,
674
- lg_attack_step : LanguageGraphAttackStep ,
675
- node_id : Optional [int ] = None ,
676
- model_asset : Optional [ModelAsset ] = None ,
677
- defense_status : Optional [float ] = None ,
678
- existence_status : Optional [bool ] = None
679
- ) -> AttackGraphNode :
680
- """Create and add a node to the graph
681
- Arguments:
682
- lg_attack_step - the language graph attack step that corresponds
683
- to the attack graph node to create
684
- node_id - id to assign to the newly created node, usually
685
- provided only when loading an existing attack
686
- graph from a file. If not provided the id will
687
- be set to the next highest id available.
688
- model_asset - the model asset that corresponds to the attack
689
- step node. While optional it is highly
690
- recommended that this be provided. It should
691
- only be ommitted if the model which was used to
692
- generate the attack graph is not available when
693
- loading an attack graph from a file.
694
- defese_status - the defense status of the node. Only, relevant
695
- for defense type nodes. A value between 0.0 and
696
- 1.0 is expected.
697
- existence_status - the existence status of the node. Only, relevant
698
- for exist and notExist type nodes.
699
-
700
- Return:
701
- The newly created attack step node.
702
- """
703
- node_id = node_id if node_id is not None else self .next_node_id
704
- if node_id in self .nodes :
705
- raise ValueError (f'Node index { node_id } already in use.' )
706
- self .next_node_id = max (node_id + 1 , self .next_node_id )
707
-
708
- if logger .isEnabledFor (logging .DEBUG ):
709
- # Avoid running json.dumps when not in debug
710
- logger .debug ('Create and add to attackgraph node of type "%s" '
711
- 'with id:%d.\n ' % (
712
- lg_attack_step .full_name ,
713
- node_id
714
- ))
715
-
716
-
717
- node = AttackGraphNode (
718
- node_id = node_id ,
719
- lg_attack_step = lg_attack_step ,
720
- model_asset = model_asset ,
721
- defense_status = defense_status ,
722
- existence_status = existence_status
723
- )
724
-
725
- self .nodes [node_id ] = node
726
- self ._full_name_to_node [node .full_name ] = node
727
-
728
- return node
729
-
730
662
def remove_node (self , node : AttackGraphNode ) -> None :
731
663
"""Remove node from attack graph
732
664
Arguments:
@@ -748,9 +680,8 @@ def remove_node(self, node: AttackGraphNode) -> None:
748
680
def add_attacker (
749
681
self ,
750
682
attacker : Attacker ,
751
- attacker_id : Optional [int ] = None ,
752
- entry_points : list [int ] = [],
753
- reached_attack_steps : list [int ] = []
683
+ entry_points : list [int ] = None ,
684
+ reached_attack_steps : list [int ] = None
754
685
):
755
686
"""Add an attacker to the graph
756
687
Arguments:
@@ -765,33 +696,19 @@ def add_attacker(
765
696
"""
766
697
767
698
if logger .isEnabledFor (logging .DEBUG ):
768
- # Avoid running json.dumps when not in debug
769
- if attacker_id is not None :
770
- logger .debug ('Add attacker "%s" with id:%d.' ,
771
- attacker .name ,
772
- attacker_id
773
- )
774
- else :
775
- logger .debug ('Add attacker "%s" without id.' ,
776
- attacker .name
777
- )
778
-
779
- attacker .id = attacker_id or self .next_attacker_id
780
- if attacker .id in self .attackers :
781
- raise ValueError (f'Attacker index { attacker_id } already in use.' )
699
+ logger .debug ('Add attacker "%s" with id:%d.' , attacker .name , attacker .id )
782
700
783
- self .next_attacker_id = max (attacker .id + 1 , self .next_attacker_id )
784
- for node_id in reached_attack_steps :
785
- node = self .nodes [node_id ]
701
+ for node_id in reached_attack_steps or []:
702
+ node = self .get_node_by_id (node_id )
786
703
if node :
787
704
attacker .compromise (node )
788
705
else :
789
706
msg = ("Could not find node with id %d"
790
707
"in reached attack steps." )
791
708
logger .error (msg , node_id )
792
709
raise AttackGraphException (msg % node_id )
793
- for node_id in entry_points :
794
- node = self .nodes [ node_id ]
710
+ for node_id in entry_points or [] :
711
+ node = self .get_node_by_id ( int ( node_id ))
795
712
if node :
796
713
attacker .entry_points .add (node )
797
714
else :
0 commit comments