atomicshop 2.12.18__py3-none-any.whl → 2.12.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.12.18'
4
+ __version__ = '2.12.20'
@@ -102,8 +102,10 @@ def write_file(
102
102
  content = '\n'.join(content)
103
103
  else:
104
104
  file_object.writelines(content)
105
-
106
- if isinstance(content, str):
105
+ elif isinstance(content, str):
106
+ file_object.write(content)
107
+ # THis will happen if the content is bytes and the file mode is 'wb'.
108
+ elif isinstance(content, bytes) and 'b' in file_mode:
107
109
  file_object.write(content)
108
110
  else:
109
111
  raise TypeError(f"Content type is not supported: {type(content)}")
@@ -1,8 +1,6 @@
1
1
  from typing import Literal, Union
2
- from pathlib import Path
3
2
 
4
- from .checks import dns, network, hash, process_running
5
- from .. import filesystem, scheduling
3
+ from .checks import dns, network, file, url, process_running
6
4
 
7
5
 
8
6
  DNS__DEFAULT_SETTINGS = {
@@ -122,36 +120,31 @@ class ChangeMonitor:
122
120
 
123
121
  # === Additional variables ========================================
124
122
 
125
- # self.original_object_directory = None
126
- # self.original_object_file_path = None
127
-
128
- # # If 'store_original_object' is True, create directory for original object.
129
- # if self.store_original_object:
130
- # # Make path for original object.
131
- # self.original_object_directory = filesystem.add_object_to_path(
132
- # self.input_directory, 'Original')
133
- # # Create directory if it doesn't exist.
134
- # filesystem.create_directory(self.original_object_directory)
135
- #
136
- # Initialize objects for DNS and Network monitoring.
137
-
138
123
  self.checks_instance = None
139
124
  self._setup_object()
140
125
 
141
126
  def _setup_object(self):
142
- if self.object_type == 'file' or 'url_' in self.object_type:
143
- self.checks_instance = hash
144
- if self.object_type == 'dns':
145
- self.checks_instance = dns
127
+ if self.object_type == 'file':
128
+ if not self.object_type_settings:
129
+ self.object_type_settings = FILE__URL__DEFAULT_SETTINGS
146
130
 
131
+ self.checks_instance = file.FileCheck(self)
132
+ elif self.object_type.startswith('url_'):
133
+ if not self.object_type_settings:
134
+ self.object_type_settings = FILE__URL__DEFAULT_SETTINGS
135
+
136
+ self.checks_instance = url.UrlCheck(self)
137
+ elif self.object_type == 'dns':
147
138
  if not self.object_type_settings:
148
139
  self.object_type_settings = DNS__DEFAULT_SETTINGS
140
+
141
+ self.checks_instance = dns.DnsCheck(self)
149
142
  elif self.object_type == 'network':
150
- self.checks_instance = network
143
+ self.checks_instance = network.NetworkCheck(self)
151
144
  elif self.object_type == 'process_running':
152
- self.checks_instance = process_running
153
-
154
- self.checks_instance.setup_check(self)
145
+ self.checks_instance = process_running.ProcessRunningCheck(self)
146
+ else:
147
+ raise ValueError(f"ERROR: Unknown object type: {self.object_type}")
155
148
 
156
149
  def check_cycle(self, print_kwargs: dict = None):
157
150
  """
@@ -1,128 +1,137 @@
1
1
  from pathlib import Path
2
+ from typing import Union
2
3
 
3
4
  from ...etw.dns_trace import DnsTrace
4
5
  from ...print_api import print_api
5
- from ...diff_check import DiffChecker
6
+ from ...import diff_check
6
7
 
7
8
 
8
9
  INPUT_FILE_DEFAULT_NAME: str = 'known_domains.json'
9
10
  INPUT_STATISTICS_FILE_DEFAULT_NAME: str = 'dns_statistics.json'
10
- FETCH_ENGINE: DnsTrace = DnsTrace(enable_process_poller=True, attrs=['name', 'cmdline', 'domain', 'query_type'])
11
- SETTINGS = {}
12
- DIFF_CHECKER_AGGREGATION = DiffChecker(
13
- check_object=list(), # DNS events will be appended to this list.
14
- return_first_cycle=True,
15
- operation_type='new_objects'
16
- )
17
- DIFF_CHECKER_STATISTICS = DiffChecker(
18
- check_object=list(), # DNS events will be appended to this list.
19
- return_first_cycle=True,
20
- operation_type='hit_statistics',
21
- hit_statistics_enable_queue=True
22
- )
23
11
 
24
12
 
25
- def setup_check(change_monitor_instance):
26
- global SETTINGS
27
- SETTINGS = change_monitor_instance.object_type_settings
28
-
29
- if SETTINGS['alert_always'] and SETTINGS['alert_about_missing_entries_after_learning']:
30
- raise ValueError(
31
- "ERROR: [alert_always] and [alert_about_missing_entries_after_learning] cannot be True at the same time.")
32
-
33
- if not change_monitor_instance.input_file_name:
34
- change_monitor_instance.input_file_name = INPUT_FILE_DEFAULT_NAME
35
- input_file_path = (
36
- str(Path(change_monitor_instance.input_directory, change_monitor_instance.input_file_name)))
37
-
38
- if not change_monitor_instance.input_statistics_file_name:
39
- change_monitor_instance.input_statistics_file_name = INPUT_STATISTICS_FILE_DEFAULT_NAME
40
- input_statistic_file_path = (
41
- str(Path(change_monitor_instance.input_directory, change_monitor_instance.input_statistics_file_name)))
42
-
43
- if SETTINGS['learning_mode_create_unique_entries_list']:
44
- DIFF_CHECKER_AGGREGATION.input_file_write_only = change_monitor_instance.input_file_write_only
45
- DIFF_CHECKER_AGGREGATION.check_object_display_name = \
46
- f'{change_monitor_instance.input_file_name}|{change_monitor_instance.object_type}'
47
- DIFF_CHECKER_AGGREGATION.input_file_path = input_file_path
48
- DIFF_CHECKER_AGGREGATION.new_objects_hours_then_difference = SETTINGS['learning_hours']
49
- DIFF_CHECKER_AGGREGATION.initiate_before_action()
50
-
51
- if SETTINGS['create_alert_statistics']:
52
- DIFF_CHECKER_STATISTICS.input_file_write_only = change_monitor_instance.input_file_write_only
53
- DIFF_CHECKER_STATISTICS.check_object_display_name = \
54
- f'{change_monitor_instance.input_statistics_file_name}|{change_monitor_instance.object_type}'
55
- DIFF_CHECKER_STATISTICS.input_file_path = input_statistic_file_path
56
- DIFF_CHECKER_STATISTICS.hit_statistics_input_file_rotation_cycle_hours = SETTINGS['statistics_rotation_hours']
57
- DIFF_CHECKER_STATISTICS.initiate_before_action()
58
-
59
- # Start DNS monitoring.
60
- FETCH_ENGINE.start()
61
-
62
-
63
- def execute_cycle(print_kwargs: dict = None) -> list:
13
+ class DnsCheck:
64
14
  """
65
- This function executes the cycle of the change monitor: dns.
66
- The function is blocking so while using it, the script will wait for the next DNS event.
67
- No need to use 'time.sleep()'.
68
-
69
- :param print_kwargs: print_api kwargs.
70
- :return: List of dictionaries with the results of the cycle.
15
+ Class for DNS monitoring.
71
16
  """
72
-
73
- # 'emit()' method is blocking (it uses 'get' of queue instance)
74
- # will return a dict with current DNS trace event.
75
- event_dict = FETCH_ENGINE.emit()
76
-
77
- return_list = list()
78
- if SETTINGS['learning_mode_create_unique_entries_list']:
79
- _aggregation_process(event_dict, return_list, print_kwargs)
80
-
81
- if SETTINGS['create_alert_statistics'] and SETTINGS['alert_always']:
82
- _statistics_process(event_dict, return_list, print_kwargs)
83
-
84
- return return_list
85
-
86
-
87
- def _aggregation_process(event_dict: dict, return_list: list, print_kwargs: dict = None):
88
- DIFF_CHECKER_AGGREGATION.check_object = [event_dict]
89
-
90
- # Check if 'known_domains' list was updated from previous cycle.
91
- result, message = DIFF_CHECKER_AGGREGATION.check_list_of_dicts(
92
- sort_by_keys=['cmdline', 'name'], print_kwargs=print_kwargs)
93
-
94
- if result:
95
- # Check if 'updated' key is in the result. This means that this is a regular cycle.
96
- if 'updated' in result:
97
- if not result['time_passed']:
98
- # Get list of new connections only.
99
- # new_connections_only: list = list_of_dicts.get_difference(result['old'], result['updated'])
100
-
101
- for connection in result['updated']:
102
- message = \
103
- f"[Learning] New Domain Added: {connection['name']} | " \
104
- f"{connection['domain']} | {connection['query_type']} | " \
105
- f"{connection['cmdline']}"
106
- print_api(message, color='yellow', **(print_kwargs or {}))
107
-
108
- return_list.append(message)
109
-
110
- # Check if learning time passed, so we can alert the new entries.
111
- if 'time_passed' in result:
112
- if result['time_passed']:
113
- _statistics_process(result['updated'], return_list, print_kwargs)
114
-
115
-
116
- def _statistics_process(event_dict: dict, return_list: list, print_kwargs: dict = None):
117
- DIFF_CHECKER_STATISTICS.check_object = [event_dict]
118
-
119
- # Check if 'known_domains' list was updated from previous cycle.
120
- result, message = DIFF_CHECKER_STATISTICS.check_list_of_dicts(
121
- sort_by_keys=['cmdline', 'name'], print_kwargs=print_kwargs)
122
-
123
- if result:
124
- if 'count' in result:
125
- message = f"[Alert] DNS Request: {result['entry']} | Times hit: {result['count']}"
126
- print_api(message, color='yellow', **(print_kwargs or {}))
127
-
128
- return_list.append(message)
17
+
18
+ def __init__(self, change_monitor_instance):
19
+ self.change_monitor_instance = change_monitor_instance
20
+ self.diff_checker_aggregation: Union[diff_check.DiffChecker, None] = None
21
+ self.diff_checker_statistics: Union[diff_check.DiffChecker, None] = None
22
+ self.settings: dict = change_monitor_instance.object_type_settings
23
+ self.fetch_engine: DnsTrace = (
24
+ DnsTrace(enable_process_poller=True, attrs=['name', 'cmdline', 'domain', 'query_type']))
25
+
26
+ if self.settings['alert_always'] and self.settings['alert_about_missing_entries_after_learning']:
27
+ raise ValueError(
28
+ "ERROR: [alert_always] and [alert_about_missing_entries_after_learning] "
29
+ "cannot be True at the same time.")
30
+
31
+ if not change_monitor_instance.input_file_name:
32
+ change_monitor_instance.input_file_name = INPUT_FILE_DEFAULT_NAME
33
+ input_file_path = (
34
+ str(Path(change_monitor_instance.input_directory, change_monitor_instance.input_file_name)))
35
+
36
+ if not change_monitor_instance.input_statistics_file_name:
37
+ change_monitor_instance.input_statistics_file_name = INPUT_STATISTICS_FILE_DEFAULT_NAME
38
+ input_statistic_file_path = (
39
+ str(Path(change_monitor_instance.input_directory, change_monitor_instance.input_statistics_file_name)))
40
+
41
+ if self.settings['learning_mode_create_unique_entries_list']:
42
+ aggregation_display_name = \
43
+ f'{change_monitor_instance.input_file_name}|{change_monitor_instance.object_type}'
44
+ self.diff_checker_aggregation = diff_check.DiffChecker(
45
+ check_object=list(), # DNS events will be appended to this list.
46
+ return_first_cycle=True,
47
+ operation_type='new_objects',
48
+ input_file_write_only=change_monitor_instance.input_file_write_only,
49
+ check_object_display_name=aggregation_display_name,
50
+ input_file_path=input_file_path,
51
+ new_objects_hours_then_difference=self.settings['learning_hours']
52
+ )
53
+ self.diff_checker_aggregation.initiate_before_action()
54
+
55
+ if self.settings['create_alert_statistics']:
56
+ statistics_display_name = \
57
+ f'{change_monitor_instance.input_statistics_file_name}|{change_monitor_instance.object_type}'
58
+
59
+ self.diff_checker_statistics = diff_check.DiffChecker(
60
+ check_object=list(), # DNS events will be appended to this list.
61
+ return_first_cycle=True,
62
+ operation_type='hit_statistics',
63
+ hit_statistics_enable_queue=True,
64
+ input_file_write_only=change_monitor_instance.input_file_write_only,
65
+ check_object_display_name=statistics_display_name,
66
+ input_file_path=input_statistic_file_path,
67
+ hit_statistics_input_file_rotation_cycle_hours=self.settings['statistics_rotation_hours']
68
+ )
69
+ self.diff_checker_statistics.initiate_before_action()
70
+
71
+ # Start DNS monitoring.
72
+ self.fetch_engine.start()
73
+
74
+ def execute_cycle(self, print_kwargs: dict = None) -> list:
75
+ """
76
+ This function executes the cycle of the change monitor: dns.
77
+ The function is blocking so while using it, the script will wait for the next DNS event.
78
+ No need to use 'time.sleep()'.
79
+
80
+ :param print_kwargs: print_api kwargs.
81
+ :return: List of dictionaries with the results of the cycle.
82
+ """
83
+
84
+ # 'emit()' method is blocking (it uses 'get' of queue instance)
85
+ # will return a dict with current DNS trace event.
86
+ event_dict = self.fetch_engine.emit()
87
+
88
+ return_list = list()
89
+ if self.settings['learning_mode_create_unique_entries_list']:
90
+ self._aggregation_process(event_dict, return_list, print_kwargs)
91
+
92
+ if self.settings['create_alert_statistics'] and self.settings['alert_always']:
93
+ self._statistics_process(event_dict, return_list, print_kwargs)
94
+
95
+ return return_list
96
+
97
+ def _aggregation_process(self, event_dict: dict, return_list: list, print_kwargs: dict = None):
98
+ self.diff_checker_aggregation.check_object = [event_dict]
99
+
100
+ # Check if 'known_domains' list was updated from previous cycle.
101
+ result, message = self.diff_checker_aggregation.check_list_of_dicts(
102
+ sort_by_keys=['cmdline', 'name'], print_kwargs=print_kwargs)
103
+
104
+ if result:
105
+ # Check if 'updated' key is in the result. This means that this is a regular cycle.
106
+ if 'updated' in result:
107
+ if not result['time_passed']:
108
+ # Get list of new connections only.
109
+ # new_connections_only: list = list_of_dicts.get_difference(result['old'], result['updated'])
110
+
111
+ for connection in result['updated']:
112
+ message = \
113
+ f"[Learning] New Domain Added: {connection['name']} | " \
114
+ f"{connection['domain']} | {connection['query_type']} | " \
115
+ f"{connection['cmdline']}"
116
+ print_api(message, color='yellow', **(print_kwargs or {}))
117
+
118
+ return_list.append(message)
119
+
120
+ # Check if learning time passed, so we can alert the new entries.
121
+ if 'time_passed' in result:
122
+ if result['time_passed']:
123
+ self._statistics_process(result['updated'], return_list, print_kwargs)
124
+
125
+ def _statistics_process(self, event_dict: dict, return_list: list, print_kwargs: dict = None):
126
+ self.diff_checker_statistics.check_object = [event_dict]
127
+
128
+ # Check if 'known_domains' list was updated from previous cycle.
129
+ result, message = self.diff_checker_statistics.check_list_of_dicts(
130
+ sort_by_keys=['cmdline', 'name'], print_kwargs=print_kwargs)
131
+
132
+ if result:
133
+ if 'count' in result:
134
+ message = f"[Alert] DNS Request: {result['entry']} | Times hit: {result['count']}"
135
+ print_api(message, color='yellow', **(print_kwargs or {}))
136
+
137
+ return_list.append(message)
@@ -0,0 +1,77 @@
1
+ from pathlib import Path
2
+
3
+ from ... import filesystem, hashing
4
+ from ... import diff_check
5
+ from ...print_api import print_api
6
+
7
+
8
+ class FileCheck:
9
+ """
10
+ Class for file monitoring.
11
+ """
12
+ def __init__(self, change_monitor_instance):
13
+ self.diff_checker = None
14
+ self.change_monitor_instance = None
15
+ self.store_original_file_path = None
16
+
17
+ if not change_monitor_instance.input_file_name:
18
+ change_monitor_instance.input_file_name = Path(change_monitor_instance.check_object).name
19
+ change_monitor_instance.input_file_name = change_monitor_instance.input_file_name.lower()
20
+ change_monitor_instance.input_file_name = (
21
+ change_monitor_instance.input_file_name.replace(' ', '_').replace('.', '_'))
22
+ change_monitor_instance.input_file_name = f'{change_monitor_instance.input_file_name}.txt'
23
+
24
+ input_file_path = (
25
+ str(Path(change_monitor_instance.input_directory, change_monitor_instance.input_file_name)))
26
+
27
+ # If 'store_original_object' is True, create filename for the store original object.
28
+ if change_monitor_instance.object_type_settings['store_original_object']:
29
+ store_original_file_name: str = f'ORIGINAL_{Path(change_monitor_instance.check_object).name}'
30
+ self.store_original_file_path = str(Path(change_monitor_instance.input_directory, store_original_file_name))
31
+
32
+ self.diff_checker = diff_check.DiffChecker(
33
+ return_first_cycle=False,
34
+ operation_type='single_object',
35
+ input_file_path=input_file_path,
36
+ check_object_display_name=f'{change_monitor_instance.input_file_name}|{change_monitor_instance.object_type}'
37
+ )
38
+ self.diff_checker.initiate_before_action()
39
+ self.change_monitor_instance = change_monitor_instance
40
+
41
+ def execute_cycle(self, print_kwargs: dict = None):
42
+ """
43
+ This function executes the cycle of the change monitor: hash.
44
+
45
+ :param print_kwargs: print_api kwargs.
46
+ :return: List of dictionaries with the results of the cycle.
47
+ """
48
+
49
+ return_list = list()
50
+
51
+ self._get_hash()
52
+
53
+ # Check if the object was updated.
54
+ result, message = self.diff_checker.check_string(
55
+ print_kwargs=print_kwargs)
56
+
57
+ # If the object was updated, print the message in yellow color, otherwise print in green color.
58
+ if result:
59
+ print_api(message, color='yellow', **print_kwargs)
60
+ # create_message_file(message, self.__class__.__name__, logger=self.logger)
61
+
62
+ return_list.append(message)
63
+ else:
64
+ print_api(message, color='green', **print_kwargs)
65
+
66
+ return return_list
67
+
68
+ def _get_hash(self):
69
+ # Copy the file to the original object directory.
70
+ if self.store_original_file_path:
71
+ filesystem.copy_file(self.change_monitor_instance.check_object, self.store_original_file_path)
72
+
73
+ # Get hash of the file.
74
+ hash_string = hashing.hash_file(self.change_monitor_instance.check_object)
75
+
76
+ # Set the hash string to the 'check_object' variable.
77
+ self.diff_checker.check_object = hash_string
@@ -1,18 +1,17 @@
1
+ from ... import diff_check
1
2
  from ...print_api import print_api
2
3
  from .hash_checks import file, url
3
4
 
4
5
 
5
- def setup_check(change_monitor_instance):
6
- # if store_original_object and not (input_directory or input_file_path):
7
- # raise ValueError(
8
- # 'ERROR: if [store_original_object] is True, either '
9
- # '[input_directory] or [input_file_path] must be specified .')
6
+ DIFF_CHECKER = diff_check.DiffChecker(
7
+ return_first_cycle=False,
8
+ operation_type='single_object'
9
+ )
10
10
 
11
11
 
12
+ def setup_check(change_monitor_instance):
12
13
  change_monitor_instance.set_input_file_path()
13
14
 
14
- change_monitor_instance.diff_checker.operation_type = 'single_object'
15
-
16
15
  if change_monitor_instance.object_type == 'file':
17
16
  file.setup_check(change_monitor_instance, change_monitor_instance.check_object)
18
17
  elif 'url_' in change_monitor_instance.object_type:
@@ -1,91 +1,91 @@
1
+ from pathlib import Path
2
+ from typing import Union
3
+
1
4
  from ...wrappers.psutilw import psutilw
2
5
  from ...basics import list_of_dicts
3
6
  from ...print_api import print_api
7
+ from ... import diff_check
4
8
 
5
9
 
6
- def setup_check(change_monitor_instance):
7
- original_name: str = str()
8
-
9
- # Initialize objects for network monitoring.
10
- change_monitor_instance.fetch_engine = psutilw.PsutilConnections()
11
-
12
- # Change settings for the DiffChecker object.
13
- change_monitor_instance.diff_checker.return_first_cycle = True
14
-
15
- if change_monitor_instance.generate_input_file_name:
16
- original_name = 'known_connections'
17
- # Make path for 'input_file_name'.
18
- change_monitor_instance.input_file_name = f'{original_name}.txt'
19
-
20
- change_monitor_instance.diff_checker.check_object_display_name = \
21
- f'{original_name}|{change_monitor_instance.object_type}'
22
-
23
- # Set the 'check_object' to empty list, since we will append the list of DNS events.
24
- change_monitor_instance.diff_checker.check_object = list()
25
-
26
- change_monitor_instance.diff_checker.operation_type = 'single_object'
27
-
28
-
29
- def execute_cycle(change_monitor_instance, print_kwargs: dict = None):
30
- """
31
- This function executes the cycle of the change monitor: network.
32
-
33
- :param change_monitor_instance: Instance of the ChangeMonitor class.
34
- :param print_kwargs: print_api kwargs.
35
-
36
- :return: List of dictionaries with the results of the cycle.
37
- """
38
-
39
- if print_kwargs is None:
40
- print_kwargs = dict()
41
-
42
- return_list = list()
43
-
44
- _get_list(change_monitor_instance)
45
-
46
- change_monitor_instance._set_input_file_path()
47
-
48
- # Check if 'known_domains' list was updated from previous cycle.
49
- result, message = change_monitor_instance.diff_checker.check_list_of_dicts(print_kwargs=print_kwargs)
10
+ INPUT_FILE_DEFAULT_NAME: str = 'known_connections.txt'
50
11
 
51
- if result:
52
- # Get list of new connections only.
53
- new_connections_only: list = list_of_dicts.get_difference(result['old'], result['updated'])
54
12
 
55
- for connection in new_connections_only:
56
- message = \
57
- f"New connection: {connection['name']} | " \
58
- f"{connection['dst_ip']}:{connection['dst_port']} | " \
59
- f"{connection['family']} | {connection['type']} | {connection['cmdline']}"
60
- # f"{connection['src_ip']}:{connection['src_port']} -> " \
61
- print_api(message, color='yellow', **print_kwargs)
62
-
63
- return_list.append(message)
64
-
65
- return return_list
66
-
67
-
68
- def _get_list(change_monitor_instance):
13
+ class NetworkCheck:
69
14
  """
70
- The function will get the list of opened network sockets and return only the new ones.
71
-
72
- :param change_monitor_instance: Instance of the ChangeMonitor class.
73
-
74
- :return: list of dicts, of new network sockets.
15
+ Class for network monitoring.
75
16
  """
76
17
 
77
- # Get all connections (list of dicts), including process name and cmdline.
78
- connections_list_of_dicts: list = \
79
- change_monitor_instance.fetch_engine.get_connections_with_process_as_list_of_dicts(
80
- attrs=['name', 'cmdline', 'family', 'type', 'dst_ip', 'dst_port'], skip_empty_dst=True,
81
- cmdline_to_string=True, remove_duplicates=True)
82
-
83
- # Get list of connections that are not in 'known_connections' list.
84
- missing_connections_from_cycle: list = list_of_dicts.get_difference(
85
- change_monitor_instance.diff_checker.check_object, connections_list_of_dicts)
86
- # Add missing new connections to 'known_connections' list.
87
- change_monitor_instance.diff_checker.check_object.extend(missing_connections_from_cycle)
88
-
89
- # Sort list of dicts by process name and then by process cmdline.
90
- change_monitor_instance.diff_checker.check_object = list_of_dicts.sort_by_keys(
91
- change_monitor_instance.diff_checker.check_object, key_list=['cmdline', 'name'], case_insensitive=True)
18
+ def __init__(self, change_monitor_instance):
19
+ self.change_monitor_instance = change_monitor_instance
20
+ self.diff_checker: Union[diff_check.DiffChecker, None] = None
21
+ self.fetch_engine = psutilw.PsutilConnections()
22
+
23
+ if not change_monitor_instance.input_file_name:
24
+ change_monitor_instance.input_file_name = INPUT_FILE_DEFAULT_NAME
25
+ input_file_path = (
26
+ str(Path(change_monitor_instance.input_directory, change_monitor_instance.input_file_name)))
27
+
28
+ diff_checker_display_name = \
29
+ f'{change_monitor_instance.input_file_name}|{change_monitor_instance.object_type}'
30
+ self.diff_checker = diff_check.DiffChecker(
31
+ check_object=list(), # we will append the list of connection events.
32
+ return_first_cycle=True,
33
+ operation_type='single_object',
34
+ check_object_display_name=diff_checker_display_name,
35
+ input_file_path=input_file_path
36
+ )
37
+ self.diff_checker.initiate_before_action()
38
+
39
+ def execute_cycle(self, print_kwargs: dict = None):
40
+ """
41
+ This function executes the cycle of the change monitor: network.
42
+
43
+ :param print_kwargs: print_api kwargs.
44
+ :return: List of dictionaries with the results of the cycle.
45
+ """
46
+
47
+ return_list = list()
48
+
49
+ self._get_list()
50
+
51
+ # Check if 'known_domains' list was updated from previous cycle.
52
+ result, message = self.diff_checker.check_list_of_dicts(print_kwargs=print_kwargs)
53
+
54
+ if result:
55
+ # Get list of new connections only.
56
+ new_connections_only: list = list_of_dicts.get_difference(result['old'], result['updated'])
57
+
58
+ for connection in new_connections_only:
59
+ message = \
60
+ f"New connection: {connection['name']} | " \
61
+ f"{connection['dst_ip']}:{connection['dst_port']} | " \
62
+ f"{connection['family']} | {connection['type']} | {connection['cmdline']}"
63
+ # f"{connection['src_ip']}:{connection['src_port']} -> " \
64
+ print_api(message, color='yellow', **(print_kwargs or {}))
65
+
66
+ return_list.append(message)
67
+
68
+ return return_list
69
+
70
+ def _get_list(self):
71
+ """
72
+ The function will get the list of opened network sockets and return only the new ones.
73
+
74
+ :return: list of dicts, of new network sockets.
75
+ """
76
+
77
+ # Get all connections (list of dicts), including process name and cmdline.
78
+ connections_list_of_dicts: list = \
79
+ self.fetch_engine.get_connections_with_process_as_list_of_dicts(
80
+ attrs=['name', 'cmdline', 'family', 'type', 'dst_ip', 'dst_port'], skip_empty_dst=True,
81
+ cmdline_to_string=True, remove_duplicates=True)
82
+
83
+ # Get list of connections that are not in 'known_connections' list.
84
+ missing_connections_from_cycle: list = list_of_dicts.get_difference(
85
+ self.diff_checker.check_object, connections_list_of_dicts)
86
+ # Add missing new connections to 'known_connections' list.
87
+ self.diff_checker.check_object.extend(missing_connections_from_cycle)
88
+
89
+ # Sort list of dicts by process name and then by process cmdline.
90
+ self.diff_checker.check_object = list_of_dicts.sort_by_keys(
91
+ self.diff_checker.check_object, key_list=['cmdline', 'name'], case_insensitive=True)