Skip to content

Commit

Permalink
Rework attack graph deepcopy implementation to make it less recursive.
Browse files Browse the repository at this point in the history
Note: This version does not work with the current implemenation of the MAL
Simulator. Amend this commit when we dig deeper into this.
  • Loading branch information
andrewbwm committed Sep 23, 2024
1 parent 9f8fca8 commit 4ba4d64
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 9 deletions.
16 changes: 16 additions & 0 deletions maltoolbox/attackgraph/attacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from __future__ import annotations
from dataclasses import dataclass, field
import copy
import logging

from typing import Optional
Expand Down Expand Up @@ -41,6 +42,21 @@ def to_dict(self) -> dict:
def __repr__(self) -> str:
return str(self.to_dict())

def __deepcopy__(self, memo):
if id(self) in memo:
return memo[id(self)]

copied_attacker = Attacker(
self.name,
[],
[],
self.id
)

memo[id(self)] = copied_attacker

return copied_attacker

def compromise(self, node: AttackGraphNode) -> None:
"""
Have the attacke compromise the node given as a parameter.
Expand Down
26 changes: 24 additions & 2 deletions maltoolbox/attackgraph/attackgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,33 @@ def __deepcopy__(self, memo):
copied_attackgraph = AttackGraph(self.lang_graph)
copied_attackgraph.model = self.model

copied_attackgraph.nodes = []

# Deep copy nodes and add references to them
copied_attackgraph.nodes = copy.deepcopy(self.nodes, memo)
for node in self.nodes:
copied_node = copy.deepcopy(node, memo)
copied_attackgraph.nodes.append(copied_node)

for node in self.nodes:
if node.parents:
memo[id(node)].parents = copy.deepcopy(node.parents, memo)
if node.children:
memo[id(node)].children = copy.deepcopy(node.children, memo)
if node.compromised_by:
memo[id(node)].compromised_by = copy.deepcopy(
node.compromised_by, memo)

# copied_attackgraph.nodes = copy.deepcopy(self.nodes, memo)

# Deep copy attackers and references to them
copied_attackgraph.attackers = copy.deepcopy(self.attackers, memo)
copied_attackgraph.attackers = []
for attacker in self.attackers:
copied_attacker = copy.deepcopy(attacker, memo)
copied_attacker.entry_points = copy.deepcopy(
attacker.entry_points)
copied_attacker.reached_attack_steps = copy.deepcopy(
attacker.reached_attack_steps)
copied_attackgraph.attackers.append(copied_attacker)

# Copy lookup dicts
copied_attackgraph._id_to_attacker = \
Expand Down
11 changes: 4 additions & 7 deletions maltoolbox/attackgraph/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ def __repr__(self) -> str:
def __deepcopy__(self, memo) -> AttackGraphNode:
"""Deep copy an attackgraph node"""

if id(self) in memo:
return memo[id(self)]

copied_node = AttackGraphNode(
self.type,
self.name,
Expand All @@ -85,7 +88,7 @@ def __deepcopy__(self, memo) -> AttackGraphNode:
self.existence_status,
self.is_viable,
self.is_necessary,
copy.deepcopy(self.compromised_by, memo),
[],
self.mitre_info,
copy.deepcopy(self.tags, memo),
copy.deepcopy(self.attributes, memo),
Expand All @@ -95,12 +98,6 @@ def __deepcopy__(self, memo) -> AttackGraphNode:
# Remember that self was already copied
memo[id(self)] = copied_node

# Deep copy children and parents, send memo (avoid infinite recursion)
if self.parents:
copied_node.parents = copy.deepcopy(self.parents, memo)
if self.children:
copied_node.children = copy.deepcopy(self.children, memo)

return copied_node

def is_compromised(self) -> bool:
Expand Down

0 comments on commit 4ba4d64

Please sign in to comment.