#1961: scanning no longer happens every timestep - the scan is all done in one timestep after the required timestep countdown is complete

This commit is contained in:
Czar.Echavez
2023-10-30 15:34:13 +00:00
parent 1ddf400d6f
commit 98ca33e994
5 changed files with 150 additions and 73 deletions

View File

@@ -98,6 +98,7 @@ class FileSystemItemABC(SimComponent):
state["status"] = self.health_status.name state["status"] = self.health_status.name
state["visible_status"] = self.visible_health_status.name state["visible_status"] = self.visible_health_status.name
state["previous_hash"] = self.previous_hash state["previous_hash"] = self.previous_hash
state["revealed_to_red"] = self.revealed_to_red
return state return state
def _init_request_manager(self) -> RequestManager: def _init_request_manager(self) -> RequestManager:
@@ -124,10 +125,6 @@ class FileSystemItemABC(SimComponent):
""" """
return convert_size(self.size) return convert_size(self.size)
def reveal_to_red(self):
"""Reveals the folder/file to the red agent."""
self.revealed_to_red = True
@abstractmethod @abstractmethod
def check_hash(self) -> bool: def check_hash(self) -> bool:
""" """
@@ -246,15 +243,23 @@ class FileSystem(SimComponent):
for folder_id in self.folders: for folder_id in self.folders:
self.folders[folder_id].apply_timestep(timestep=timestep) self.folders[folder_id].apply_timestep(timestep=timestep)
def scan(self): def scan(self, instant_scan: bool = False):
"""Scan all the folders (and child files) in the file system.""" """
for folder_id in self.folders: Scan all the folders (and child files) in the file system.
self.folders[folder_id].scan()
def reveal_to_red(self): :param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False.
"""Reveals all the folders (and child files) in the file system to the red agent.""" """
for folder_id in self.folders: for folder_id in self.folders:
self.folders[folder_id].reveal_to_red() self.folders[folder_id].scan(instant_scan=instant_scan)
def reveal_to_red(self, instant_scan: bool = False):
"""
Reveals all the folders (and child files) in the file system to the red agent.
:param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False.
"""
for folder_id in self.folders:
self.folders[folder_id].reveal_to_red(instant_scan=instant_scan)
def create_folder(self, folder_name: str) -> Folder: def create_folder(self, folder_name: str) -> Folder:
""" """
@@ -529,20 +534,25 @@ class Folder(FileSystemItemABC):
# scan files each timestep # scan files each timestep
if self.scan_duration >= 0: if self.scan_duration >= 0:
# scan one file per timestep self.scan_duration -= 1
file = self.get_file_by_id(file_uuid=list(self.files)[self.scan_duration])
if self.scan_duration == 0:
for file_id in self.files:
file = self.get_file_by_id(file_uuid=file_id)
file.scan() file.scan()
if file.visible_health_status == FileSystemItemHealthStatus.CORRUPT: if file.visible_health_status == FileSystemItemHealthStatus.CORRUPT:
self.visible_health_status = FileSystemItemHealthStatus.CORRUPT self.visible_health_status = FileSystemItemHealthStatus.CORRUPT
self.scan_duration -= 1
# red scan file at each step # red scan file at each step
if self.red_scan_duration >= 0: if self.red_scan_duration >= 0:
# scan one file per timestep
file = self.get_file_by_id(file_uuid=list(self.files)[self.red_scan_duration])
file.reveal_to_red()
self.red_scan_duration -= 1 self.red_scan_duration -= 1
if self.red_scan_duration == 0:
self.revealed_to_red = True
for file_id in self.files:
file = self.get_file_by_id(file_uuid=file_id)
file.reveal_to_red()
# apply timestep to files in folder # apply timestep to files in folder
for file_id in self.files: for file_id in self.files:
self.files[file_id].apply_timestep(timestep=timestep) self.files[file_id].apply_timestep(timestep=timestep)
@@ -641,23 +651,44 @@ class Folder(FileSystemItemABC):
"""Returns true if the folder is being quarantined.""" """Returns true if the folder is being quarantined."""
pass pass
def scan(self) -> None: def scan(self, instant_scan: bool = False) -> None:
"""Update Folder visible status.""" """
Update Folder visible status.
:param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False.
"""
if instant_scan:
for file_id in self.files:
file = self.get_file_by_id(file_uuid=file_id)
file.scan()
if file.visible_health_status == FileSystemItemHealthStatus.CORRUPT:
self.visible_health_status = FileSystemItemHealthStatus.CORRUPT
return
if self.scan_duration <= 0: if self.scan_duration <= 0:
# scan one file per timestep # scan one file per timestep
self.scan_duration = len(self.files) - 1 self.scan_duration = len(self.files)
self.fs.sys_log.info(f"Scanning folder {self.name} (id: {self.uuid})") self.fs.sys_log.info(f"Scanning folder {self.name} (id: {self.uuid})")
else: else:
# scan already in progress # scan already in progress
self.fs.sys_log.info(f"Scan is already in progress {self.name} (id: {self.uuid})") self.fs.sys_log.info(f"Scan is already in progress {self.name} (id: {self.uuid})")
def reveal_to_red(self): def reveal_to_red(self, instant_scan: bool = False):
"""Reveals the folders and files to the red agent.""" """
super().reveal_to_red() Reveals the folders and files to the red agent.
:param: instant_scan: If True, the scan is completed instantly and ignores scan duration. Default False.
"""
if instant_scan:
self.revealed_to_red = True
for file_id in self.files:
file = self.get_file_by_id(file_uuid=file_id)
file.reveal_to_red()
return
if self.red_scan_duration <= 0: if self.red_scan_duration <= 0:
# scan one file per timestep # scan one file per timestep
self.red_scan_duration = len(self.files) - 1 self.red_scan_duration = len(self.files)
self.fs.sys_log.info(f"Folder revealed to red agent: {self.name} (id: {self.uuid})") self.fs.sys_log.info(f"Folder revealed to red agent: {self.name} (id: {self.uuid})")
else: else:
# scan already in progress # scan already in progress
@@ -830,6 +861,10 @@ class File(FileSystemItemABC):
self.folder.fs.sys_log.info(f"Scanning file {self.sim_path if self.sim_path else path}") self.folder.fs.sys_log.info(f"Scanning file {self.sim_path if self.sim_path else path}")
self.visible_health_status = self.health_status self.visible_health_status = self.health_status
def reveal_to_red(self):
"""Reveals the folder/file to the red agent."""
self.revealed_to_red = True
def check_hash(self) -> bool: def check_hash(self) -> bool:
""" """
Check if the file has been changed. Check if the file has been changed.

View File

@@ -929,6 +929,15 @@ class Node(SimComponent):
is_resetting: bool = False is_resetting: bool = False
"If true, the node will try turning itself off then back on again." "If true, the node will try turning itself off then back on again."
node_scan_duration: int = 10
"How many timesteps until the whole node is scanned. Default 10 time steps."
node_scan_countdown: int = 0
"Time steps until scan is complete"
red_scan_countdown: int = 0
"Time steps until reveal to red scan is complete."
def __init__(self, **kwargs): def __init__(self, **kwargs):
""" """
Initialize the Node with various components and managers. Initialize the Node with various components and managers.
@@ -1098,8 +1107,47 @@ class Node(SimComponent):
self.is_resetting = False self.is_resetting = False
self.power_on() self.power_on()
# apply time step to node components # time steps which require the node to be on
if self.operating_state == NodeOperatingState.ON: if self.operating_state == NodeOperatingState.ON:
# node scanning
if self.node_scan_countdown > 0:
self.node_scan_countdown -= 1
if self.node_scan_countdown == 0:
# scan everything!
for process_id in self.processes:
self.processes[process_id].scan()
# scan services
for service_id in self.services:
self.services[service_id].scan()
# scan applications
for application_id in self.applications:
self.applications[application_id].scan()
# scan file system
self.file_system.scan(instant_scan=True)
if self.red_scan_countdown > 0:
self.red_scan_countdown -= 1
if self.red_scan_countdown == 0:
# scan processes
for process_id in self.processes:
self.processes[process_id].reveal_to_red()
# scan services
for service_id in self.services:
self.services[service_id].reveal_to_red()
# scan applications
for application_id in self.applications:
self.applications[application_id].reveal_to_red()
# scan file system
self.file_system.reveal_to_red(instant_scan=True)
for process_id in self.processes: for process_id in self.processes:
self.processes[process_id].apply_timestep(timestep=timestep) self.processes[process_id].apply_timestep(timestep=timestep)
@@ -1124,20 +1172,7 @@ class Node(SimComponent):
to the red agent. to the red agent.
""" """
# scan processes self.node_scan_countdown = self.node_scan_duration
for process_id in self.processes:
self.processes[process_id].scan()
# scan services
for service_id in self.services:
self.services[service_id].scan()
# scan applications
for application_id in self.applications:
self.applications[application_id].scan()
# scan file system
self.file_system.scan()
def reveal_to_red(self) -> None: def reveal_to_red(self) -> None:
""" """
@@ -1152,20 +1187,7 @@ class Node(SimComponent):
`revealed_to_red` to `True`. `revealed_to_red` to `True`.
""" """
# scan processes self.red_scan_countdown = self.node_scan_duration
for process_id in self.processes:
self.processes[process_id].reveal_to_red()
# scan services
for service_id in self.services:
self.services[service_id].reveal_to_red()
# scan applications
for application_id in self.applications:
self.applications[application_id].reveal_to_red()
# scan file system
self.file_system.reveal_to_red()
def power_on(self): def power_on(self):
"""Power on the Node, enabling its NICs if it is in the OFF state.""" """Power on the Node, enabling its NICs if it is in the OFF state."""

View File

@@ -215,8 +215,8 @@ def test_folder_scan(file_system):
folder.apply_timestep(timestep=0) folder.apply_timestep(timestep=0)
assert folder.health_status == FileSystemItemHealthStatus.CORRUPT assert folder.health_status == FileSystemItemHealthStatus.CORRUPT
assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD
assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD
assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD
folder.apply_timestep(timestep=1) folder.apply_timestep(timestep=1)
@@ -226,12 +226,33 @@ def test_folder_scan(file_system):
assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT
assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT
folder.apply_timestep(timestep=2)
assert folder.health_status == FileSystemItemHealthStatus.CORRUPT def test_folder_reveal_to_red_scan(file_system):
assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT """Test the ability to reveal files to red."""
assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT folder: Folder = file_system.create_folder(folder_name="test_folder")
assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT file_system.create_file(file_name="test_file.txt", folder_name="test_folder")
file_system.create_file(file_name="test_file2.txt", folder_name="test_folder")
file1: File = folder.get_file_by_id(file_uuid=list(folder.files)[1])
file2: File = folder.get_file_by_id(file_uuid=list(folder.files)[0])
assert folder.revealed_to_red is False
assert file1.revealed_to_red is False
assert file2.revealed_to_red is False
folder.reveal_to_red()
folder.apply_timestep(timestep=0)
assert folder.revealed_to_red is False
assert file1.revealed_to_red is False
assert file2.revealed_to_red is False
folder.apply_timestep(timestep=1)
assert folder.revealed_to_red is True
assert file1.revealed_to_red is True
assert file2.revealed_to_red is True
def test_simulated_file_check_hash(file_system): def test_simulated_file_check_hash(file_system):

View File

@@ -47,8 +47,8 @@ def test_folder_scan_request(populated_file_system):
folder.apply_timestep(timestep=0) folder.apply_timestep(timestep=0)
assert folder.health_status == FileSystemItemHealthStatus.CORRUPT assert folder.health_status == FileSystemItemHealthStatus.CORRUPT
assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD
assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert file1.visible_health_status == FileSystemItemHealthStatus.GOOD
assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD assert file2.visible_health_status == FileSystemItemHealthStatus.GOOD
folder.apply_timestep(timestep=1) folder.apply_timestep(timestep=1)
@@ -58,13 +58,6 @@ def test_folder_scan_request(populated_file_system):
assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT
assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT
folder.apply_timestep(timestep=2)
assert folder.health_status == FileSystemItemHealthStatus.CORRUPT
assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT
assert file1.visible_health_status == FileSystemItemHealthStatus.CORRUPT
assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT
def test_file_checkhash_request(populated_file_system): def test_file_checkhash_request(populated_file_system):
"""Test that an agent can request a file hash check.""" """Test that an agent can request a file hash check."""

View File

@@ -69,14 +69,16 @@ def test_node_os_scan(node, service, application):
assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD
file: File = node.file_system.create_file(folder_name="test_folder", file_name="file.txt") file: File = node.file_system.create_file(folder_name="test_folder", file_name="file.txt")
file2: File = node.file_system.create_file(folder_name="test_folder", file_name="file2.txt")
file.corrupt() file.corrupt()
file2.corrupt()
assert file.visible_health_status == FileSystemItemHealthStatus.GOOD assert file.visible_health_status == FileSystemItemHealthStatus.GOOD
# run os scan # run os scan
node.apply_request(["os", "scan"]) node.apply_request(["os", "scan"])
# apply time steps # apply time steps
for i in range(20): for i in range(10):
node.apply_timestep(timestep=i) node.apply_timestep(timestep=i)
# should update the state of all items # should update the state of all items
@@ -85,6 +87,7 @@ def test_node_os_scan(node, service, application):
assert application.health_state_visible == SoftwareHealthState.COMPROMISED assert application.health_state_visible == SoftwareHealthState.COMPROMISED
assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT
assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT
assert file2.visible_health_status == FileSystemItemHealthStatus.CORRUPT
def test_node_red_scan(node, service, application): def test_node_red_scan(node, service, application):
@@ -108,13 +111,15 @@ def test_node_red_scan(node, service, application):
assert folder.revealed_to_red is False assert folder.revealed_to_red is False
file: File = node.file_system.create_file(folder_name="test_folder", file_name="file.txt") file: File = node.file_system.create_file(folder_name="test_folder", file_name="file.txt")
file2: File = node.file_system.create_file(folder_name="test_folder", file_name="file2.txt")
assert file.revealed_to_red is False assert file.revealed_to_red is False
assert file2.revealed_to_red is False
# run os scan # run os scan
node.apply_request(["scan"]) node.apply_request(["scan"])
# apply time steps # apply time steps
for i in range(20): for i in range(10):
node.apply_timestep(timestep=i) node.apply_timestep(timestep=i)
# should update the state of all items # should update the state of all items
@@ -123,6 +128,7 @@ def test_node_red_scan(node, service, application):
assert application.revealed_to_red is True assert application.revealed_to_red is True
assert folder.revealed_to_red is True assert folder.revealed_to_red is True
assert file.revealed_to_red is True assert file.revealed_to_red is True
assert file2.revealed_to_red is True
def test_reset_node(node): def test_reset_node(node):