Merge pull request #1752 from guardicore/1695-removing-system-info-infra

1695 removing system info infrastructure
This commit is contained in:
VakarisZ 2022-03-01 14:58:04 +02:00 committed by GitHub
commit 1b484e0365
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 27 additions and 487 deletions

View File

@ -101,7 +101,6 @@ class Configuration(object):
finger_classes = [] finger_classes = []
exploiter_classes = [] exploiter_classes = []
system_info_collector_classes = []
# depth of propagation # depth of propagation
depth = 2 depth = 2

View File

@ -12,7 +12,6 @@ from ctypes import c_char_p
from common.utils.attack_utils import ScanStatus, UsageEnum from common.utils.attack_utils import ScanStatus, UsageEnum
from infection_monkey.config import WormConfiguration from infection_monkey.config import WormConfiguration
from infection_monkey.system_info import OperatingSystem, SystemInfoCollector
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
from infection_monkey.utils.commands import ( from infection_monkey.utils.commands import (
build_monkey_commandline_explicitly, build_monkey_commandline_explicitly,

View File

@ -139,7 +139,7 @@ class AutomatedMaster(IMaster):
credential_collector_thread = create_daemon_thread( credential_collector_thread = create_daemon_thread(
target=self._run_plugins, target=self._run_plugins,
args=( args=(
config["system_info_collector_classes"], config["credential_collector_classes"],
"credential collector", "credential collector",
self._collect_credentials, self._collect_credentials,
), ),

View File

@ -1,6 +0,0 @@
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
# Import all actions as modules
hiddenimports = collect_submodules("infection_monkey.system_info.collectors")
# Add action files that we enumerate
datas = collect_data_files("infection_monkey.system_info.collectors", include_py_files=True)

View File

@ -1,112 +0,0 @@
import glob
import logging
import os
import pwd
from common.utils.attack_utils import ScanStatus
from infection_monkey.telemetry.attack.t1005_telem import T1005Telem
logger = logging.getLogger(__name__)
class SSHCollector(object):
"""
SSH keys and known hosts collection module
"""
default_dirs = ["/.ssh/", "/"]
@staticmethod
def get_info():
logger.info("Started scanning for ssh keys")
home_dirs = SSHCollector.get_home_dirs()
ssh_info = SSHCollector.get_ssh_files(home_dirs)
logger.info("Scanned for ssh keys")
return ssh_info
@staticmethod
def get_ssh_struct(name, home_dir):
"""
:return: SSH info struct with these fields:
name: username of user, for whom the keys belong
home_dir: users home directory
public_key: contents of *.pub file(public key)
private_key: contents of * file(private key)
known_hosts: contents of known_hosts file(all the servers keys are good for,
possibly hashed)
"""
return {
"name": name,
"home_dir": home_dir,
"public_key": None,
"private_key": None,
"known_hosts": None,
}
@staticmethod
def get_home_dirs():
root_dir = SSHCollector.get_ssh_struct("root", "")
home_dirs = [
SSHCollector.get_ssh_struct(x.pw_name, x.pw_dir)
for x in pwd.getpwall()
if x.pw_dir.startswith("/home")
]
home_dirs.append(root_dir)
return home_dirs
@staticmethod
def get_ssh_files(usr_info):
for info in usr_info:
path = info["home_dir"]
for directory in SSHCollector.default_dirs:
if os.path.isdir(path + directory):
try:
current_path = path + directory
# Searching for public key
if glob.glob(os.path.join(current_path, "*.pub")):
# Getting first file in current path with .pub extension(public key)
public = glob.glob(os.path.join(current_path, "*.pub"))[0]
logger.info("Found public key in %s" % public)
try:
with open(public) as f:
info["public_key"] = f.read()
# By default private key has the same name as public,
# only without .pub
private = os.path.splitext(public)[0]
if os.path.exists(private):
try:
with open(private) as f:
# no use from ssh key if it's encrypted
private_key = f.read()
if private_key.find("ENCRYPTED") == -1:
info["private_key"] = private_key
logger.info("Found private key in %s" % private)
T1005Telem(
ScanStatus.USED, "SSH key", "Path: %s" % private
).send()
else:
continue
except (IOError, OSError):
pass
# By default known hosts file is called 'known_hosts'
known_hosts = os.path.join(current_path, "known_hosts")
if os.path.exists(known_hosts):
try:
with open(known_hosts) as f:
info["known_hosts"] = f.read()
logger.info("Found known_hosts in %s" % known_hosts)
except (IOError, OSError):
pass
# If private key found don't search more
if info["private_key"]:
break
except (IOError, OSError):
pass
except OSError:
pass
usr_info = [
info
for info in usr_info
if info["private_key"] or info["known_hosts"] or info["public_key"]
]
return usr_info

View File

@ -1,76 +0,0 @@
import logging
import sys
from enum import IntEnum
import psutil
from infection_monkey.network.info import get_host_subnets
from infection_monkey.system_info.system_info_collectors_handler import SystemInfoCollectorsHandler
logger = logging.getLogger(__name__)
# Linux doesn't have WindowsError
try:
WindowsError
except NameError:
# noinspection PyShadowingBuiltins
WindowsError = psutil.AccessDenied
class OperatingSystem(IntEnum):
Windows = 0
Linux = 1
class SystemInfoCollector(object):
"""
A class that checks the current operating system and calls system information collecting
modules accordingly
"""
def __init__(self):
self.os = SystemInfoCollector.get_os()
if OperatingSystem.Windows == self.os:
from .windows_info_collector import WindowsInfoCollector
self.collector = WindowsInfoCollector()
else:
from .linux_info_collector import LinuxInfoCollector
self.collector = LinuxInfoCollector()
def get_info(self):
return self.collector.get_info()
@staticmethod
def get_os():
if sys.platform.startswith("win"):
return OperatingSystem.Windows
else:
return OperatingSystem.Linux
class InfoCollector(object):
"""
Generic Info Collection module
"""
def __init__(self):
self.info = {"credentials": {}}
def get_info(self):
# Collect all hardcoded
self.get_network_info()
# Collect all plugins
SystemInfoCollectorsHandler().execute_all_configured()
def get_network_info(self):
"""
Adds network information from the host to the system information.
Currently updates with list of networks accessible from host
containing host ip and the subnet range
:return: None. Updates class information
"""
logger.debug("Reading subnets")
self.info["network_info"] = {"networks": get_host_subnets()}

View File

@ -1,3 +0,0 @@
"""
This package holds all the dynamic (plugin) collectors
"""

View File

@ -1,26 +0,0 @@
import logging
from infection_monkey.system_info import InfoCollector
from infection_monkey.system_info.SSH_info_collector import SSHCollector
logger = logging.getLogger(__name__)
class LinuxInfoCollector(InfoCollector):
"""
System information collecting module for Linux operating systems
"""
def __init__(self):
super(LinuxInfoCollector, self).__init__()
def get_info(self):
"""
Collect Linux system information
Hostname, process list and network subnets
:return: Dict of system information
"""
logger.debug("Running Linux collector")
super(LinuxInfoCollector, self).get_info()
self.info["ssh_info"] = SSHCollector.get_info()
return self.info

View File

@ -1,42 +0,0 @@
from abc import ABCMeta, abstractmethod
import infection_monkey.system_info.collectors
from infection_monkey.config import WormConfiguration
from infection_monkey.utils.plugins.plugin import Plugin
class SystemInfoCollector(Plugin, metaclass=ABCMeta):
"""
ABC for system info collection. See system_info_collector_handler for more info. Basically,
to implement a new system info
collector, inherit from this class in an implementation in the
infection_monkey.system_info.collectors class, and override
the 'collect' method. Don't forget to parse your results in the Monkey Island and to add the
collector to the configuration
as well - see monkey_island.cc.services.processing.system_info_collectors for examples.
See the Wiki page "How to add a new System Info Collector to the Monkey?" for a detailed guide.
"""
def __init__(self, name="unknown"):
self.name = name
@staticmethod
def should_run(class_name) -> bool:
return class_name in WormConfiguration.system_info_collector_classes
@staticmethod
def base_package_file():
return infection_monkey.system_info.collectors.__file__
@staticmethod
def base_package_name():
return infection_monkey.system_info.collectors.__package__
@abstractmethod
def collect(self) -> dict:
"""
Collect the relevant information and return it in a dictionary.
To be implemented by each collector.
"""
raise NotImplementedError()

View File

@ -1,35 +0,0 @@
import logging
from typing import Sequence
from infection_monkey.system_info.system_info_collector import SystemInfoCollector
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
logger = logging.getLogger(__name__)
class SystemInfoCollectorsHandler(object):
def __init__(self):
self.collectors_list = self.config_to_collectors_list()
def execute_all_configured(self):
successful_collections = 0
system_info_telemetry = {}
for collector in self.collectors_list:
try:
logger.debug("Executing system info collector: '{}'".format(collector.name))
collected_info = collector.collect()
system_info_telemetry[collector.name] = collected_info
successful_collections += 1
except Exception as e:
# If we failed one collector, no need to stop execution. Log and continue.
logger.error("Collector {} failed. Error info: {}".format(collector.name, e))
logger.info(
"All system info collectors executed. Total {} executed, out of which {} "
"collected successfully.".format(len(self.collectors_list), successful_collections)
)
SystemInfoTelem({"collectors": system_info_telemetry}).send()
@staticmethod
def config_to_collectors_list() -> Sequence[SystemInfoCollector]:
return SystemInfoCollector.get_instances()

View File

@ -1,53 +0,0 @@
import logging
import sys
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR
from infection_monkey.credential_collectors.windows_cred_collector.mimikatz_cred_collector import (
MimikatzCredentialCollector,
)
sys.coinit_flags = 0 # needed for proper destruction of the wmi python module
import infection_monkey.config # noqa: E402
from infection_monkey.system_info import InfoCollector # noqa: E402
logger = logging.getLogger(__name__)
logger.info("started windows info collector")
class WindowsInfoCollector(InfoCollector):
"""
System information collecting module for Windows operating systems
"""
def __init__(self):
super(WindowsInfoCollector, self).__init__()
self._config = infection_monkey.config.WormConfiguration
def get_info(self):
"""
Collect Windows system information
Hostname, process list and network subnets
Tries to read credential secrets using mimikatz
:return: Dict of system information
"""
logger.debug("Running Windows collector")
super(WindowsInfoCollector, self).get_info()
# TODO: Think about returning self.get_wmi_info()
from infection_monkey.config import WormConfiguration
if MIMIKATZ_COLLECTOR in WormConfiguration.system_info_collector_classes:
self.get_mimikatz_info()
return self.info
def get_mimikatz_info(self):
logger.info("Gathering mimikatz info")
try:
credentials = MimikatzCredentialCollector.get_creds()
if credentials:
self.info["credentials"].update(credentials)
logger.info("Mimikatz info gathered successfully")
else:
logger.info("No mimikatz info was gathered")
except Exception as e:
logger.info(f"Mimikatz credential collector failed: {e}")

View File

@ -1,20 +0,0 @@
from common.common_consts.telem_categories import TelemCategoryEnum
from infection_monkey.telemetry.base_telem import BaseTelem
class SystemInfoTelem(BaseTelem):
def __init__(self, system_info):
"""
Default system info telemetry constructor
:param system_info: System info returned from SystemInfoCollector.get_info()
"""
super(SystemInfoTelem, self).__init__()
self.system_info = system_info
telem_category = TelemCategoryEnum.SYSTEM_INFO
def get_data(self):
return self.system_info
def send(self, log_data=False):
super(SystemInfoTelem, self).send(log_data)

View File

@ -93,8 +93,8 @@ class TelemetryFeed(flask_restful.Resource):
return "Monkey discovered machine %s." % telem["data"]["machine"]["ip_addr"] return "Monkey discovered machine %s." % telem["data"]["machine"]["ip_addr"]
@staticmethod @staticmethod
def get_systeminfo_telem_brief(telem): def get_credentials_telem_brief(_):
return "Monkey collected system information." return "Monkey collected stole some credentials."
@staticmethod @staticmethod
def get_trace_telem_brief(telem): def get_trace_telem_brief(telem):
@ -114,11 +114,11 @@ class TelemetryFeed(flask_restful.Resource):
TELEM_PROCESS_DICT = { TELEM_PROCESS_DICT = {
TelemCategoryEnum.TUNNEL: TelemetryFeed.get_tunnel_telem_brief, TelemCategoryEnum.CREDENTIALS: TelemetryFeed.get_credentials_telem_brief,
TelemCategoryEnum.STATE: TelemetryFeed.get_state_telem_brief,
TelemCategoryEnum.EXPLOIT: TelemetryFeed.get_exploit_telem_brief, TelemCategoryEnum.EXPLOIT: TelemetryFeed.get_exploit_telem_brief,
TelemCategoryEnum.SCAN: TelemetryFeed.get_scan_telem_brief,
TelemCategoryEnum.SYSTEM_INFO: TelemetryFeed.get_systeminfo_telem_brief,
TelemCategoryEnum.TRACE: TelemetryFeed.get_trace_telem_brief,
TelemCategoryEnum.POST_BREACH: TelemetryFeed.get_post_breach_telem_brief, TelemCategoryEnum.POST_BREACH: TelemetryFeed.get_post_breach_telem_brief,
TelemCategoryEnum.SCAN: TelemetryFeed.get_scan_telem_brief,
TelemCategoryEnum.STATE: TelemetryFeed.get_state_telem_brief,
TelemCategoryEnum.TRACE: TelemetryFeed.get_trace_telem_brief,
TelemCategoryEnum.TUNNEL: TelemetryFeed.get_tunnel_telem_brief,
} }

View File

@ -1,13 +1,13 @@
from monkey_island.cc.services.config_schema.basic import BASIC from monkey_island.cc.services.config_schema.basic import BASIC
from monkey_island.cc.services.config_schema.basic_network import BASIC_NETWORK from monkey_island.cc.services.config_schema.basic_network import BASIC_NETWORK
from monkey_island.cc.services.config_schema.definitions.credential_collector_classes import (
CREDENTIAL_COLLECTOR_CLASSES,
)
from monkey_island.cc.services.config_schema.definitions.exploiter_classes import EXPLOITER_CLASSES from monkey_island.cc.services.config_schema.definitions.exploiter_classes import EXPLOITER_CLASSES
from monkey_island.cc.services.config_schema.definitions.finger_classes import FINGER_CLASSES from monkey_island.cc.services.config_schema.definitions.finger_classes import FINGER_CLASSES
from monkey_island.cc.services.config_schema.definitions.post_breach_actions import ( from monkey_island.cc.services.config_schema.definitions.post_breach_actions import (
POST_BREACH_ACTIONS, POST_BREACH_ACTIONS,
) )
from monkey_island.cc.services.config_schema.definitions.system_info_collector_classes import (
SYSTEM_INFO_COLLECTOR_CLASSES,
)
from monkey_island.cc.services.config_schema.internal import INTERNAL from monkey_island.cc.services.config_schema.internal import INTERNAL
from monkey_island.cc.services.config_schema.monkey import MONKEY from monkey_island.cc.services.config_schema.monkey import MONKEY
from monkey_island.cc.services.config_schema.ransomware import RANSOMWARE from monkey_island.cc.services.config_schema.ransomware import RANSOMWARE
@ -20,7 +20,7 @@ SCHEMA = {
# users will not accidentally chose unsafe options # users will not accidentally chose unsafe options
"definitions": { "definitions": {
"exploiter_classes": EXPLOITER_CLASSES, "exploiter_classes": EXPLOITER_CLASSES,
"system_info_collector_classes": SYSTEM_INFO_COLLECTOR_CLASSES, "credential_collector_classes": CREDENTIAL_COLLECTOR_CLASSES,
"post_breach_actions": POST_BREACH_ACTIONS, "post_breach_actions": POST_BREACH_ACTIONS,
"finger_classes": FINGER_CLASSES, "finger_classes": FINGER_CLASSES,
}, },

View File

@ -1,8 +1,8 @@
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR from common.common_consts.credential_collector_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
SYSTEM_INFO_COLLECTOR_CLASSES = { CREDENTIAL_COLLECTOR_CLASSES = {
"title": "System Information Collectors", "title": "Credential Collectors",
"description": "Click on a system info collector to find out what it collects.", "description": "Click on a credential collector to find out what it collects.",
"type": "string", "type": "string",
"anyOf": [ "anyOf": [
{ {

View File

@ -1,4 +1,4 @@
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR from common.common_consts.credential_collector_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
MONKEY = { MONKEY = {
"title": "Monkey", "title": "Monkey",
@ -73,15 +73,15 @@ MONKEY = {
}, },
}, },
}, },
"system_info": { "credential_collectors": {
"title": "System info", "title": "Credential collection",
"type": "object", "type": "object",
"properties": { "properties": {
"system_info_collector_classes": { "credential_collector_classes": {
"title": "System info collectors", "title": "Credential collectors",
"type": "array", "type": "array",
"uniqueItems": True, "uniqueItems": True,
"items": {"$ref": "#/definitions/system_info_collector_classes"}, "items": {"$ref": "#/definitions/credential_collector_classes"},
"default": [ "default": [
MIMIKATZ_COLLECTOR, MIMIKATZ_COLLECTOR,
SSH_COLLECTOR, SSH_COLLECTOR,

View File

@ -1,59 +0,0 @@
import logging
import typing
logger = logging.getLogger(__name__)
SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS = {}
class SystemInfoTelemetryDispatcher(object):
def __init__(
self,
collector_to_parsing_functions: typing.Mapping[str, typing.List[typing.Callable]] = None,
):
"""
:param collector_to_parsing_functions: Map between collector names and a list of functions
that process the output of that collector.
If `None` is supplied, uses the default one; This should be the normal flow, overriding the
collector->functions mapping is useful mostly for testing.
"""
if collector_to_parsing_functions is None:
collector_to_parsing_functions = SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS
self.collector_to_processing_functions = collector_to_parsing_functions
def dispatch_collector_results_to_relevant_processors(self, telemetry_json):
"""
If the telemetry has collectors' results, dispatches the results to the relevant
processing functions.
:param telemetry_json: Telemetry sent from the Monkey
"""
if "collectors" in telemetry_json["data"]:
self.dispatch_single_result_to_relevant_processor(telemetry_json)
def dispatch_single_result_to_relevant_processor(self, telemetry_json):
relevant_monkey_guid = telemetry_json["monkey_guid"]
for collector_name, collector_results in telemetry_json["data"]["collectors"].items():
self.dispatch_result_of_single_collector_to_processing_functions(
collector_name, collector_results, relevant_monkey_guid
)
def dispatch_result_of_single_collector_to_processing_functions(
self, collector_name, collector_results, relevant_monkey_guid
):
if collector_name in self.collector_to_processing_functions:
for processing_function in self.collector_to_processing_functions[collector_name]:
# noinspection PyBroadException
try:
processing_function(collector_results, relevant_monkey_guid)
except Exception as e:
logger.error(
"Error {} while processing {} system info telemetry".format(
str(e), collector_name
),
exc_info=True,
)
else:
logger.warning("Unknown system info collector name: {}".format(collector_name))

View File

@ -94,8 +94,8 @@ export default function UiSchema(props) {
'ui:emptyValue': '' 'ui:emptyValue': ''
} }
}, },
system_info: { credential_collectors: {
system_info_collector_classes: { credential_collector_classes: {
classNames: 'config-template-no-header', classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect 'ui:widget': AdvancedMultiSelect
} }

View File

@ -16,9 +16,9 @@ function getPluginDescriptors(schema, config) {
selectedPlugins: config.monkey.post_breach.post_breach_actions selectedPlugins: config.monkey.post_breach.post_breach_actions
}, },
{ {
name: 'SystemInfoCollectors', name: 'CredentialCollectors',
allPlugins: schema.definitions.system_info_collector_classes.anyOf, allPlugins: schema.definitions.credential_collector_classes.anyOf,
selectedPlugins: config.monkey.system_info.system_info_collector_classes selectedPlugins: config.monkey.credential_collectors.credential_collector_classes
} }
]); ]);
} }

View File

@ -1,20 +0,0 @@
import json
import pytest
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
SYSTEM_INFO = {}
@pytest.fixture
def system_info_telem_test_instance():
return SystemInfoTelem(SYSTEM_INFO)
def test_system_info_telem_send(system_info_telem_test_instance, spy_send_telemetry):
system_info_telem_test_instance.send()
expected_data = SYSTEM_INFO
expected_data = json.dumps(expected_data, cls=system_info_telem_test_instance.json_encoder)
assert spy_send_telemetry.data == expected_data
assert spy_send_telemetry.telem_category == "system_info"

View File

@ -94,7 +94,6 @@ Timestomping # unused class (monkey/infection_monkey/post_breach/actions/timest
SignedScriptProxyExecution # unused class (monkey/infection_monkey/post_breach/actions/use_signed_scripts.py:15) SignedScriptProxyExecution # unused class (monkey/infection_monkey/post_breach/actions/use_signed_scripts.py:15)
EnvironmentCollector # unused class (monkey/infection_monkey/system_info/collectors/environment_collector.py:19) EnvironmentCollector # unused class (monkey/infection_monkey/system_info/collectors/environment_collector.py:19)
HostnameCollector # unused class (monkey/infection_monkey/system_info/collectors/hostname_collector.py:10) HostnameCollector # unused class (monkey/infection_monkey/system_info/collectors/hostname_collector.py:10)
_.coinit_flags # unused attribute (monkey/infection_monkey/system_info/windows_info_collector.py:11)
_.representations # unused attribute (monkey/monkey_island/cc/app.py:180) _.representations # unused attribute (monkey/monkey_island/cc/app.py:180)
_.log_message # unused method (monkey/infection_monkey/transport/http.py:188) _.log_message # unused method (monkey/infection_monkey/transport/http.py:188)
_.log_message # unused method (monkey/infection_monkey/transport/http.py:109) _.log_message # unused method (monkey/infection_monkey/transport/http.py:109)
@ -106,7 +105,6 @@ binaries # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-pyps
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.exploit.py:3) hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.exploit.py:3)
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py:3) hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py:3)
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.actions.py:4) hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.actions.py:4)
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.system_info.collectors.py:4)
_.wShowWindow # unused attribute (monkey/infection_monkey/monkey.py:345) _.wShowWindow # unused attribute (monkey/infection_monkey/monkey.py:345)
_.dwFlags # unused attribute (monkey/infection_monkey/monkey.py:344) _.dwFlags # unused attribute (monkey/infection_monkey/monkey.py:344)
_.do_get # unused method (monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py:79) _.do_get # unused method (monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py:79)
@ -159,10 +157,6 @@ salt # unused variable (monkey/infection_monkey/network/mysqlfinger.py:78)
thread_id # unused variable (monkey/infection_monkey/network/mysqlfinger.py:61) thread_id # unused variable (monkey/infection_monkey/network/mysqlfinger.py:61)
# leaving this since there's a TODO related to it
_.get_wmi_info # unused method (monkey/infection_monkey/system_info/windows_info_collector.py:63)
# potentially unused (there may also be unit tests referencing these) # potentially unused (there may also be unit tests referencing these)
LOG_DIR_NAME # unused variable (envs/monkey_zoo/blackbox/log_handlers/test_logs_handler.py:8) LOG_DIR_NAME # unused variable (envs/monkey_zoo/blackbox/log_handlers/test_logs_handler.py:8)
delete_logs # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:85) delete_logs # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:85)