Skip to content

Commit ec07aed

Browse files
Made many refactoring changes, fixed bugs and improved code
[__init__.py] Added new ObjectLoadError Exception [_dev.py] Improved usage of color_print, as well as added a max attempt of 10 for inputting a valid version [Flag.py] Fixed potential bug where replacing "--" can result in incorrect flags, now its lstrip('-'), Fixed bug where if a flag match was found (using cosine sim) it would still say "Sorry" and load up the ML to get the flag for you... [Logicytics.py] Fixed bug where MAX_WORKERS was str rather than int due to improper config.get call Added safe_remove and safe_append to make sure the execution list is proper and doesn't break Refactored generate_execution_list to be more efficient Performance flag now logs memory usage!!! Added simple KeyboardInterrupt warning [network_psutil.py] Made the measure method an async to improve efficiency and remove the time.sleep() bottleneck [packet_sniffer.py] Made global variables inside the Sniff class (under __init__) [vulnscan.py] Added checks for vectorizer loading to dodge crashes Improved IO bottleneck by making the write happen once in the end rather than every for-loop Signed-off-by: Shahm Najeeb <[email protected]>
1 parent 5e32c82 commit ec07aed

File tree

9 files changed

+171
-77
lines changed

9 files changed

+171
-77
lines changed

.idea/csv-editor.xml

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CODE/Logicytics.py

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import zipfile
88
from concurrent.futures import ThreadPoolExecutor, as_completed
99
from datetime import datetime
10-
from typing import Any
1110

11+
import psutil
1212
from prettytable import PrettyTable
1313

1414
from logicytics import Log, Execute, Check, Get, FileManagement, Flag, DEBUG, DELETE_LOGS
@@ -18,16 +18,33 @@
1818
ACTION, SUB_ACTION = None, None
1919
config = configparser.ConfigParser()
2020
config.read("config.ini")
21-
MAX_WORKERS = config.get("Settings", "max_workers", fallback=None)
21+
MAX_WORKERS = config.getint("Settings", "max_workers", fallback=min(32, (os.cpu_count() or 1) + 4))
2222
log.debug(f"MAX_WORKERS: {MAX_WORKERS}")
2323

2424

2525
class ExecuteScript:
2626
def __init__(self):
27-
self.execution_list = self.generate_execution_list()
27+
self.execution_list = self.__generate_execution_list()
2828

2929
@staticmethod
30-
def generate_execution_list() -> list | list[str] | list[str | Any]:
30+
def __safe_remove(file_name: str, file_list: list[str] | set[str]) -> list[str]:
31+
file_set = set(file_list)
32+
if file_name in file_set:
33+
file_set.remove(file_name)
34+
else:
35+
log.critical(f"The file {file_name} should exist in this directory - But was not found!")
36+
return list(file_set)
37+
38+
@staticmethod
39+
def __safe_append(file_name: str, file_list: list[str] | set[str]) -> list[str]:
40+
file_set = set(file_list)
41+
if os.path.exists(file_name):
42+
file_set.add(file_name)
43+
else:
44+
log.critical(f"Missing required file: {file_name}")
45+
return list(file_set)
46+
47+
def __generate_execution_list(self) -> list[str]:
3148
"""
3249
Generate an execution list of scripts based on the specified action.
3350
@@ -51,11 +68,14 @@ def generate_execution_list() -> list | list[str] | list[str | Any]:
5168
- Warns users about potential long execution times for certain actions
5269
"""
5370
execution_list = Get.list_of_files(".", extensions=(".py", ".exe", ".ps1", ".bat"))
54-
execution_list.remove("sensitive_data_miner.py")
55-
execution_list.remove("dir_list.py")
56-
execution_list.remove("tree.ps1")
57-
execution_list.remove("vulnscan.py")
58-
execution_list.remove("event_log.py")
71+
files_to_remove = {
72+
"sensitive_data_miner.py",
73+
"dir_list.py",
74+
"tree.ps1",
75+
"vulnscan.py",
76+
"event_log.py",
77+
}
78+
execution_list = [file for file in execution_list if file not in files_to_remove]
5979

6080
if ACTION == "minimal":
6181
execution_list = [
@@ -69,7 +89,7 @@ def generate_execution_list() -> list | list[str] | list[str | Any]:
6989
"event_log.py",
7090
]
7191

72-
if ACTION == "nopy":
92+
elif ACTION == "nopy":
7393
execution_list = [
7494
"browser_miner.ps1",
7595
"netadapter.ps1",
@@ -78,24 +98,36 @@ def generate_execution_list() -> list | list[str] | list[str | Any]:
7898
"tree.ps1"
7999
]
80100

81-
if ACTION == "modded":
101+
elif ACTION == "modded":
82102
# Add all files in MODS to execution list
83103
execution_list = Get.list_of_files("../MODS",
84104
extensions=(".py", ".exe", ".ps1", ".bat"),
85105
append_file_list=execution_list)
86106

87-
if ACTION == "depth":
107+
elif ACTION == "depth":
88108
log.warning(
89109
"This flag will use clunky and huge scripts, and so may take a long time, but reap great rewards.")
90-
execution_list.append("sensitive_data_miner.py")
91-
execution_list.append("dir_list.py")
92-
execution_list.append("tree.ps1")
93-
execution_list.append("event_log.py")
110+
files_to_append = {
111+
"sensitive_data_miner.py",
112+
"dir_list.py",
113+
"tree.ps1",
114+
"event_log.py",
115+
}
116+
for file in files_to_append:
117+
execution_list = self.__safe_append(file, execution_list)
94118
log.warning("This flag will use threading!")
95119

96-
if ACTION == "vulnscan_ai":
120+
elif ACTION == "vulnscan_ai":
97121
# Only vulnscan detector
98-
execution_list = ["vulnscan.py"]
122+
if os.path.exists("vulnscan.py"):
123+
execution_list = ["vulnscan.py"]
124+
else:
125+
log.critical("Vulnscan is missing...")
126+
exit(1)
127+
128+
if len(execution_list) == 0:
129+
log.critical("Nothing is in the execution list.. This is due to faulty code or corrupted Logicytics files!")
130+
exit(1)
99131

100132
log.debug(f"The following will be executed: {execution_list}")
101133
return execution_list
@@ -159,27 +191,43 @@ def __default(self):
159191

160192
def __performance(self):
161193
"""Checks performance of each script."""
194+
if DEBUG.lower() != "debug":
195+
log.warning("Advised to turn on DEBUG logging!!")
196+
162197
execution_times = []
198+
memory_usage = []
199+
process = psutil.Process()
163200

164201
for file in range(len(self.execution_list)):
165202
start_time = datetime.now()
203+
start_memory = process.memory_info().rss / 1024 / 1024 # MB
166204
log.execution(Execute.script(self.execution_list[file]))
167205
end_time = datetime.now()
206+
end_memory = process.memory_info().rss / 1024 / 1024 # MB
168207
elapsed_time = end_time - start_time
208+
memory_delta = end_memory - start_memory
209+
memory_usage.append((self.execution_list[file], str(memory_delta)))
169210
execution_times.append((self.execution_list[file], elapsed_time))
170211
log.info(f"{self.execution_list[file]} executed in {elapsed_time}")
212+
log.info(f"{self.execution_list[file]} used {memory_delta:.2f}MB of memory")
213+
log.debug(f"Started with {start_memory}MB of memory and ended with {end_memory}MB of memory")
171214

172215
table = PrettyTable()
173-
table.field_names = ["Script", "Execution Time"]
216+
table.field_names = ["Script", "Execution Time", "Memory Usage (MB)"]
174217
for script, elapsed_time in execution_times:
175-
table.add_row([script, elapsed_time])
218+
memory = next(m[1] for m in memory_usage if m[0] == script)
219+
table.add_row([script, elapsed_time, f"{memory:.2f}"])
176220

177221
try:
178222
with open(
179223
f"../ACCESS/LOGS/PERFORMANCE/Performance_Summary_"
180224
f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt", "w"
181225
) as f:
182226
f.write(table.get_string())
227+
f.write(
228+
"\nSome values may be negative, Reason may be due to external resources playing with memory usage, "
229+
"close background tasks to get more accurate readings")
230+
f.write("Note: This is not a low-level memory logger, data here isn't 100% accurate!")
183231
log.info("Performance check complete! Performance log found in ACCESS/LOGS/PERFORMANCE")
184232
except Exception as e:
185233
log.error(f"Error writing performance log: {e}")
@@ -454,7 +502,12 @@ def Logicytics():
454502

455503

456504
if __name__ == "__main__":
457-
Logicytics()
505+
try:
506+
Logicytics()
507+
except KeyboardInterrupt:
508+
log.warning("Shutting down Logicytics utility with force causes many files to remain where they shouldn't")
509+
log.warning("Please don't force shut Logicytics again - As we don't have a cleanup function yet.")
510+
exit(0)
458511
else:
459512
log.error("This script cannot be imported!")
460513
exit(1)

CODE/_dev.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,22 +125,32 @@ def _handle_file_operations() -> None:
125125
if clean_f not in files and clean_f not in EXCLUDE_FILES:
126126
removed_files.append(clean_f)
127127

128-
print("\n".join([f"\033[92m+ {file}\033[0m" for file in added_files])) # Green +
129-
print("\n".join([f"\033[91m- {file}\033[0m" for file in removed_files])) # Red -
130-
print("\n".join([f"* {file}" for file in normal_files]))
128+
for file in added_files:
129+
color_print(f"+ {file}", "green")
130+
for file in removed_files:
131+
color_print(f"- {file}", "red")
132+
for file in normal_files:
133+
print(f"* {file}")
131134

132135
if not _prompt_user("Does the list above include your added files?"):
133136
color_print("[x] Something went wrong! Please contact support.", "red")
134137
return
135138

139+
max_attempts = 10
140+
attempts = 0
136141
_update_ini_file("config.ini", files, "files")
137142
while True:
138143
version = input(f"\033[36m[?] Enter the new version of the project (Old version is {VERSION}): \033[0m")
144+
if attempts >= max_attempts:
145+
color_print("[x] Maximum attempts reached. Please run the script again.", "red")
146+
exit()
139147
if re.match(r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$', version):
140148
_update_ini_file("config.ini", version, "version")
141149
break
142150
else:
143151
color_print("[!] Please enter a valid version number (e.g., 1.2.3)", "yellow")
152+
attempts += 1
153+
color_print(f"[!] {max_attempts - attempts} attempts remaining", "yellow")
144154
color_print("\n[-] Great Job! Please tick the box in the GitHub PR request for completing steps in --dev", "green")
145155

146156

CODE/logicytics/Flag.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ def update_history(cls, user_input: str, matched_flag: str, accuracy: float):
286286
if not SAVE_PREFERENCES:
287287
return
288288
history_data = cls.load_history()
289-
matched_flag = matched_flag.replace("--", "")
289+
matched_flag = matched_flag.lstrip('-')
290290

291291
# Ensure that interactions is a dictionary (not a list)
292292
if not isinstance(history_data['interactions'], dict):
@@ -653,7 +653,8 @@ def __suggest_flag(cls, user_input: str, valid_flags: list[str]):
653653
# Get the closest valid flag match based on the user's input
654654
closest_matches = difflib.get_close_matches(user_input, valid_flags, n=1, cutoff=0.6)
655655
if closest_matches:
656-
print(f"Invalid flag '{user_input}', Did you mean '--{closest_matches[0]}'?")
656+
print(f"Invalid flag '{user_input}', Did you mean '--{closest_matches[0].replace('_', '-')}'?")
657+
exit(1)
657658

658659
# Prompt the user for a description if no close match is found
659660
user_input_desc = input("We can't find a match, Please provide a description: ").lower()
@@ -663,9 +664,10 @@ def __suggest_flag(cls, user_input: str, valid_flags: list[str]):
663664
descriptions_list = [f"Run Logicytics with {flag}" for flag in valid_flags]
664665
flag_received, accuracy_received = _Match.flag(user_input_desc, flags_list, descriptions_list)
665666
if DEBUG_MODE:
666-
print(f"User input: {user_input_desc}\nMatched flag: {flag_received}\nAccuracy: {accuracy_received:.2f}%\n")
667+
print(
668+
f"User input: {user_input_desc}\nMatched flag: {flag_received.replace('_', '-')}\nAccuracy: {accuracy_received:.2f}%\n")
667669
else:
668-
print(f"Matched flag: {flag_received} (Accuracy: {accuracy_received:.2f}%)\n")
670+
print(f"Matched flag: {flag_received.replace('_', '-')} (Accuracy: {accuracy_received:.2f}%)\n")
669671

670672
@staticmethod
671673
def show_help_menu(return_output: bool = False) -> str | None:

CODE/logicytics/__init__.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
Flag = Flag()
1818

1919

20+
class ObjectLoadError(Exception):
21+
"""Raised when an Object fails to load."""
22+
23+
def __init__(self, message="Failed to load object"):
24+
super().__init__(message)
25+
26+
2027
def config_data() -> tuple[str, str, list[str], bool]:
2128
"""
2229
Retrieves configuration data from the 'config.ini' file.
@@ -86,13 +93,13 @@ def deprecated(removal_version: str, reason: str, show_trace: bool = __show_trac
8693
def decorator(func: callable) -> callable:
8794
"""
8895
Decorator function that marks a function as deprecated and provides a warning when the function is called.
89-
96+
9097
Args:
9198
func (callable): The function to be decorated with a deprecation warning.
92-
99+
93100
Returns:
94101
callable: A wrapper function that preserves the original function's metadata and prints a deprecation warning.
95-
102+
96103
Notes:
97104
- Uses functools.wraps to preserve the original function's metadata
98105
- Prints a colorized deprecation warning to stderr
@@ -103,14 +110,14 @@ def decorator(func: callable) -> callable:
103110
def wrapper(*args, **kwargs) -> callable:
104111
"""
105112
Wraps a deprecated function to print a warning message before execution.
106-
113+
107114
Args:
108115
*args: Positional arguments passed to the original function.
109116
**kwargs: Keyword arguments passed to the original function.
110-
117+
111118
Returns:
112119
The return value of the original function after printing a deprecation warning.
113-
120+
114121
Warns:
115122
Prints a colored deprecation warning to stderr with details about:
116123
- Function name being deprecated

CODE/network_psutil.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import asyncio
12
import os
23
import socket
3-
import time
44

55
import psutil
66

@@ -125,7 +125,7 @@ def __fetch_network_connections_with_process_info(self):
125125
self.__save_data("network_connections_with_processes.txt", connections_data)
126126
log.info("Network connections with process info saved.")
127127

128-
def __measure_network_bandwidth_usage(self, sample_count: int = 5, interval: float = 1.0):
128+
async def __measure_network_bandwidth_usage(self, sample_count: int = 5, interval: float = 1.0):
129129
"""
130130
Measures and saves the average network bandwidth usage.
131131
@@ -138,7 +138,7 @@ def __measure_network_bandwidth_usage(self, sample_count: int = 5, interval: flo
138138
samples = []
139139
for _ in range(sample_count):
140140
net1 = psutil.net_io_counters()
141-
time.sleep(interval)
141+
await asyncio.sleep(interval)
142142
net2 = psutil.net_io_counters()
143143
samples.append({
144144
'up': (net2.bytes_sent - net1.bytes_sent) / 1024,

0 commit comments

Comments
 (0)