Skip to content

Commit d44ecd0

Browse files
Fixed issues caused by coderabbit
Also improved some things, Improved Logicytics.py threading Upgraded Deprecation warning with stack tracing, and added the option to remove it. Signed-off-by: Shahm Najeeb <[email protected]>
1 parent 5f1a266 commit d44ecd0

16 files changed

+201
-139
lines changed

CODE/Logicytics.py

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import os
44
import shutil
55
import subprocess
6-
import threading
76
import zipfile
7+
from concurrent.futures import ThreadPoolExecutor, as_completed
88
from datetime import datetime
99
from typing import Any
1010

@@ -225,9 +225,6 @@ def generate_execution_list() -> list | list[str] | list[str | Any]:
225225
- 'depth': Comprehensive script execution with data mining and logging scripts
226226
- 'vulnscan_ai': Vulnerability scanning script only
227227
228-
Parameters:
229-
None
230-
231228
Returns:
232229
list[str]: A list of script file paths to be executed, filtered and modified based on the current action.
233230
@@ -293,34 +290,42 @@ def execute_scripts():
293290
"""Executes the scripts in the execution list based on the action."""
294291
# Check weather to use threading or not, as well as execute code
295292
log.info("Starting Logicytics...")
293+
296294
if ACTION == "threaded" or ACTION == "depth":
297-
def threaded_execution(execution_list_thread, index_thread):
298-
log.debug(f"Thread {index_thread} started")
295+
296+
def execute_single_script(script: str) -> tuple[str, Exception | None]:
297+
"""
298+
Executes a single script and logs the result.
299+
300+
This function executes a single script and logs the result,
301+
capturing any exceptions that occur during execution
302+
303+
Parameters:
304+
script (str): The path to the script to be executed
305+
"""
306+
log.debug(f"Executing {script}")
299307
try:
300-
log.parse_execution(Execute.script(execution_list_thread[index_thread]))
301-
log.info(f"{execution_list_thread[index_thread]} executed")
302-
except UnicodeDecodeError as err:
303-
log.error(f"Error in thread: {err}")
308+
log.parse_execution(Execute.script(script))
309+
log.info(f"{script} executed")
310+
return script, None
304311
except Exception as err:
305-
log.error(f"Error in thread: {err}")
306-
log.debug(f"Thread {index_thread} finished")
312+
log.error(f"Error executing {script}: {err}")
313+
return script, err
307314

308315
log.debug("Using threading")
309-
threads = []
310316
execution_list = generate_execution_list()
311-
for index, _ in enumerate(execution_list):
312-
thread = threading.Thread(
313-
target=threaded_execution,
314-
args=(
315-
execution_list,
316-
index,
317-
),
318-
)
319-
threads.append(thread)
320-
thread.start()
321-
322-
for thread in threads:
323-
thread.join()
317+
with ThreadPoolExecutor() as executor:
318+
futures = {executor.submit(execute_single_script, script): script
319+
for script in execution_list}
320+
321+
for future in as_completed(futures):
322+
script = futures[future]
323+
result, error = future.result()
324+
if error:
325+
log.error(f"Failed to execute {script}")
326+
else:
327+
log.debug(f"Completed {script}")
328+
324329
elif ACTION == "performance_check":
325330
execution_times = []
326331
execution_list = generate_execution_list()

CODE/_debug.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,6 @@ def python_version():
185185
against predefined minimum and maximum version thresholds. It provides informative logging about
186186
the Python version status.
187187
188-
Parameters:
189-
None
190-
191188
Logs:
192189
- Info: When Python version is within the recommended range (3.11.x to 3.12.x)
193190
- Warning: When Python version is below the minimum recommended version (< 3.11)

CODE/bluetooth_details.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def _query_bluetooth_devices() -> bool | list[dict[str, str]]:
6161
capture_output=True, text=True, check=True)
6262
devices = json.loads(result.stdout)
6363
except subprocess.CalledProcessError as e:
64-
log.error(f"Failed to query Bluetooth devices: {e}")
64+
log.error(f"Failed to query Bluetooth devices with command '{command}': {e}")
6565
return False
6666
except json.JSONDecodeError as e:
6767
log.error(f"Failed to parse device information: {e}")
@@ -72,13 +72,14 @@ def _query_bluetooth_devices() -> bool | list[dict[str, str]]:
7272

7373
device_info_list = []
7474
for device in devices:
75+
FALLBACK_MSG = 'Unknown (Fallback due to failed Get request)'
7576
device_info = {
76-
'Name': device.get('FriendlyName', 'Unknown (Fallback due to failed Get request)'),
77-
'Device ID': device.get('DeviceID', 'Unknown (Fallback due to failed Get request)'),
78-
'Description': device.get('Description', 'Unknown (Fallback due to failed Get request)'),
79-
'Manufacturer': device.get('Manufacturer', 'Unknown (Fallback due to failed Get request)'),
80-
'Status': device.get('Status', 'Unknown (Fallback due to failed Get request)'),
81-
'PNP Device ID': device.get('PnpDeviceID', 'Unknown (Fallback due to failed Get request)')
77+
'Name': device.get('FriendlyName', FALLBACK_MSG),
78+
'Device ID': device.get('DeviceID', FALLBACK_MSG),
79+
'Description': device.get('Description', FALLBACK_MSG),
80+
'Manufacturer': device.get('Manufacturer', FALLBACK_MSG),
81+
'Status': device.get('Status', FALLBACK_MSG),
82+
'PNP Device ID': device.get('PnpDeviceID', FALLBACK_MSG)
8283
}
8384
log.debug(f"Retrieved device: {device_info['Name']}")
8485
device_info_list.append(device_info)

CODE/bluetooth_logger.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ def save_to_file(filename, section_title, data):
3333
file.write(f"\n{'=' * 50}\n{section_title}\n{'=' * 50}\n")
3434
file.write(f"{data}\n" if isinstance(data, str) else "\n".join(data) + "\n")
3535
file.write(f"{'=' * 50}\n")
36-
except Exception as e:
37-
log.error(f"Error writing to file {filename}: {e}")
36+
except Exception as err:
37+
log.error(f"Error writing to file {filename}: {err}")
3838

3939

4040
# Utility function to run PowerShell commands
@@ -64,8 +64,8 @@ def run_powershell_command(command):
6464
log.error(f"PowerShell command failed with return code {result.returncode}")
6565
return []
6666
return result.stdout.splitlines()
67-
except Exception as e:
68-
log.error(f"Error running PowerShell command: {e}")
67+
except Exception as err:
68+
log.error(f"Error running PowerShell command: {err}")
6969
return []
7070

7171

@@ -99,8 +99,8 @@ def parse_output(lines, regex, group_names):
9999
else:
100100
log.debug(f"Skipping unrecognized line: {line}")
101101
return results
102-
except Exception as e:
103-
log.error(f"Parsing output failed: {e}")
102+
except Exception as err:
103+
log.error(f"Parsing output failed: {err}")
104104

105105

106106
# Function to get paired Bluetooth devices
@@ -177,7 +177,7 @@ def log_bluetooth():
177177
log.debug(f"{section_title}: {paired_devices}")
178178

179179
# Collect and log event logs
180-
def collect_logs(title, command):
180+
def collect_logs(title: str, command: str):
181181
"""
182182
Collects and logs event logs by executing a PowerShell command and saving the results.
183183
@@ -215,4 +215,7 @@ def collect_logs(title, command):
215215

216216

217217
if __name__ == "__main__":
218-
log_bluetooth()
218+
try:
219+
log_bluetooth()
220+
except Exception as e:
221+
log.error(f"Failed to log Bluetooth data: {e}")

CODE/dir_list.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def run_command_threaded(directory: str, file: str, message: str, encoding: str
2727
"""
2828
log.info(f"Executing {message} for {directory}")
2929
try:
30-
command = f"powershell -Command Get-ChildItem {directory} -Recurse"
30+
safe_directory = directory.replace('"', '`"') # Escape quotes
31+
command = f'powershell -NoProfile -Command "Get-ChildItem \\""{safe_directory}\\"" -Recurse"'
3132
output = Execute.command(command)
3233
open(file, "a", encoding=encoding).write(output)
3334
log.info(f"{message} Successful for {directory} - {file}")
@@ -56,7 +57,7 @@ def command_threaded(base_directory: str, file: str, message: str, encoding: str
5657
- Handles potential errors during thread execution
5758
"""
5859
try:
59-
with ThreadPoolExecutor() as executor:
60+
with ThreadPoolExecutor(max_workers=min(32, os.cpu_count() * 4)) as executor:
6061
subdirectories = [os.path.join(base_directory, d) for d in os.listdir(base_directory) if
6162
os.path.isdir(os.path.join(base_directory, d))]
6263
futures = [executor.submit(run_command_threaded, subdir, file, message, encoding) for subdir in

CODE/dump_memory.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import platform
33
import struct
4+
from datetime import datetime
45

56
import psutil
67

@@ -15,6 +16,7 @@
1516

1617
# If the file size exceeds this limit, the file will be truncated with a message
1718
# Put 0 to disable the limit
19+
# TODO v3.3.1: Make this take from config.ini
1820
LIMIT_FILE_SIZE = 20 # Always in MiB
1921

2022

@@ -85,6 +87,9 @@ def gather_system_info():
8587
'Machine': platform.machine(),
8688
'Processor': platform.processor(),
8789
'Page Size (bytes)': struct.calcsize("P"),
90+
'CPU Count': psutil.cpu_count(),
91+
'CPU Frequency': psutil.cpu_freq().current if psutil.cpu_freq() else 'N/A',
92+
'Boot Time': datetime.fromtimestamp(psutil.boot_time()).strftime('%Y-%m-%d %H:%M:%S'),
8893
}
8994
except Exception as e:
9095
log.error(f"Error gathering system information: {e}")
@@ -134,7 +139,7 @@ def memory_dump():
134139
free_space = psutil.disk_usage(".").free
135140
if free_space < required_space:
136141
log.error(f"Not enough disk space. Need {required_space / 1024 / 1024:.2f}MB")
137-
return
142+
return
138143

139144
# Check if the memory region is readable ('r' permission)
140145
if 'r' in mem_region.perms:
@@ -161,9 +166,7 @@ def memory_dump():
161166
if mem_region.path:
162167
# Try to get device and inode-like info
163168
file_path = mem_region.path
164-
# file_device = win32api.GetFileAttributes(file_path) if os.path.exists(file_path) else 'N/A'
165169
region_metadata[' File Path'] = file_path
166-
# region_metadata['File Device'] = file_device
167170

168171
except Exception as e:
169172
log.error(f"Error adding extra file information: {str(e)}")

CODE/event_log.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ def parse_event_logs(log_type: str, output_file: str):
7979

8080
for log_type_main, output_file_main in threads_items:
8181
thread = threading.Thread(target=parse_event_logs, args=(log_type_main, output_file_main))
82+
thread.daemon = True # Don't hang if main thread exits
8283
threads.append(thread)
8384
thread.start()
8485
for thread in threads:
85-
thread.join()
86+
thread.join(timeout=600) # Wait max 10 minutes per thread
87+
if thread.is_alive():
88+
log.error(f"Thread for {thread.name} timed out (10 minutes)!")

CODE/log_miner.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ def backup_windows_logs():
1717
The function handles potential errors during log backup and logs the operation's outcome.
1818
If the backup fails, an error message is logged without raising an exception.
1919
20-
Args:
21-
None
22-
2320
Returns:
2421
None
2522

CODE/logicytics/Flag.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,16 @@ def flag(cls, user_input: str, flags: list[str], flag_description: list[str]) ->
376376
class Flag:
377377
@classmethod
378378
def __colorify(cls, text: str, color: str) -> str:
379-
The existing docstring is comprehensive and follows good documentation practices. It clearly explains the function's purpose, parameters, return value, and provides context about the color codes. Therefore:
380-
381-
KEEP_EXISTING
379+
"""
380+
Colorize text with ANSI color codes.
381+
382+
Args:
383+
text (str): The text to colorize
384+
color (str): The color code ('y' for yellow, 'r' for red, 'b' for blue)
385+
386+
Returns:
387+
str: The colorized text with ANSI escape codes
388+
"""
382389
colors = {
383390
"y": "\033[93m",
384391
"r": "\033[91m",
@@ -395,10 +402,7 @@ def __available_arguments(cls) -> tuple[argparse.Namespace, argparse.ArgumentPar
395402
This method creates an ArgumentParser with a comprehensive set of flags for customizing the application's behavior. It supports various execution modes, debugging options, system management flags, and post-execution actions.
396403
397404
The method handles argument parsing, provides helpful descriptions for each flag, and includes color-coded hints for user guidance. It also supports suggesting valid flags if an unknown flag is provided.
398-
399-
Parameters:
400-
cls (type): The class context in which the method is called.
401-
405+
402406
Returns:
403407
tuple[argparse.Namespace, argparse.ArgumentParser]: A tuple containing:
404408
- Parsed command-line arguments (Namespace)
@@ -701,10 +705,7 @@ def data(cls) -> tuple[str, str | None]:
701705
- Special flags are handled with specific logic
702706
- No invalid flag combinations are permitted
703707
- User history is optionally updated based on preferences
704-
705-
Parameters:
706-
cls (type): The class context for accessing class methods
707-
708+
708709
Returns:
709710
tuple[str, str | None]: A tuple containing:
710711
- The primary matched flag

CODE/logicytics/Get.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,29 @@
77

88
class Get:
99
@staticmethod
10-
def list_of_files(directory: str, extensions: tuple | bool = True, append_file_list: list = None,
11-
exclude_files: list = None) -> list:
10+
def list_of_files(directory: str, extensions: tuple | bool = True, append_file_list: list[str] = None,
11+
exclude_files: list[str] = None) -> list:
1212
"""
13-
Retrieves a list of files in the specified directory based on given extensions and exclusion criteria.
13+
Retrieves a list of files in the specified directory based on given extensions and exclusion criteria.
1414
15-
Supports two modes of file retrieval:
16-
1. When `extensions` is `True`, retrieves all files recursively from the directory.
17-
2. When `extensions` is a tuple, retrieves files matching specific extensions while applying exclusion rules.
15+
Supports two modes of file retrieval:
16+
1. When `extensions` is `True`, retrieves all files recursively from the directory.
17+
2. When `extensions` is a tuple, retrieves files matching specific extensions while applying exclusion rules.
1818
19-
Parameters:
20-
directory (str): Path of the directory to search for files.
21-
extensions (tuple | bool, optional): File extensions to filter or True to retrieve all files. Defaults to True.
22-
append_file_list (list, optional): Existing list to append found filenames to. Creates a new list if not provided. Defaults to None.
23-
exclude_files (list, optional): List of filenames to exclude from results. Defaults to None.
19+
Parameters:
20+
directory (str): Path of the directory to search for files.
21+
extensions (tuple | bool, optional): File extensions to filter or True to retrieve all files. Defaults to True.
22+
append_file_list (list, optional): Existing list to append found filenames to. Creates a new list if not provided. Defaults to None.
23+
exclude_files (list, optional): List of filenames to exclude from results. Defaults to None.
2424
25-
Returns:
26-
list: A list of filenames matching the specified criteria.
25+
Returns:
26+
list: A list of filenames matching the specified criteria.
2727
28-
Exclusion rules:
29-
- Ignores files starting with an underscore (_)
30-
- Excludes "Logicytics.py"
31-
- Skips files specified in `exclude_files`
32-
"""
28+
Exclusion rules:
29+
- Ignores files starting with an underscore (_)
30+
- Excludes "Logicytics.py"
31+
- Skips files specified in `exclude_files`
32+
"""
3333
append_file_list = [] if not append_file_list else append_file_list
3434

3535
if isinstance(extensions, bool) and extensions:
@@ -44,7 +44,7 @@ def list_of_files(directory: str, extensions: tuple | bool = True, append_file_l
4444
filename.endswith(extensions)
4545
and not filename.startswith("_")
4646
and filename != "Logicytics.py"
47-
and filename not in exclude_files
47+
and (exclude_files is None or filename not in exclude_files)
4848
):
4949
append_file_list.append(filename)
5050
return append_file_list

0 commit comments

Comments
 (0)