atomicshop 2.15.11__py3-none-any.whl → 3.10.5__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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/strings.py +72 -3
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -3
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +11 -6
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +1 -1
- atomicshop/diff_check.py +3 -3
- atomicshop/dns.py +128 -3
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/trace.py +40 -42
- atomicshop/etws/traces/trace_dns.py +56 -44
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +27 -5
- atomicshop/file_io/docxs.py +34 -17
- atomicshop/file_io/file_io.py +31 -17
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +616 -291
- atomicshop/get_process_list.py +3 -3
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -80
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +136 -40
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
- atomicshop/monitor/checks/dns.py +1 -1
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +39 -9
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -42
- atomicshop/process.py +24 -6
- atomicshop/process_poller/process_pool.py +0 -1
- atomicshop/process_poller/simple_process_pool.py +204 -5
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/system_resource_monitor.py +61 -47
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/urls.py +6 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +98 -27
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/githubw.py +537 -54
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/filters.py +23 -0
- atomicshop/wrappers/loggingw/formatters.py +12 -0
- atomicshop/wrappers/loggingw/handlers.py +214 -107
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +860 -22
- atomicshop/wrappers/loggingw/reading.py +134 -112
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +37 -1
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +491 -182
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +1 -1
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/pycharm.py +0 -9
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
- atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/config_editor.py +0 -37
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -268
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -151
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/install.py +0 -81
- atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
- atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
- atomicshop/wrappers/msiw.py +0 -149
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/psutilw/networks.py +0 -45
- atomicshop/wrappers/pycharmw.py +0 -81
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.15.11.dist-info/RECORD +0 -302
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
- /atomicshop/{archiver → permissions}/__init__.py +0 -0
- /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
- /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
atomicshop/on_exit.py
CHANGED
|
@@ -91,25 +91,55 @@ class ExitHandler:
|
|
|
91
91
|
# Exit the process gracefully
|
|
92
92
|
raise SystemExit(0)
|
|
93
93
|
|
|
94
|
-
def register_handlers(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
def register_handlers(
|
|
95
|
+
self,
|
|
96
|
+
at_exit: bool = True,
|
|
97
|
+
console_close: bool = True,
|
|
98
|
+
kill_signal: bool = True
|
|
99
|
+
):
|
|
100
|
+
"""
|
|
101
|
+
Register the exit handlers.
|
|
102
|
+
|
|
103
|
+
:param at_exit: Register the atexit handler.
|
|
104
|
+
Just remember that the atexit handler will be called right away on [Ctrl+C], meaning if you want to do
|
|
105
|
+
something specifically on KeyboardInterrupt, you should handle it separately and set this parameter to False.
|
|
106
|
+
Same goes for all the exceptions.
|
|
107
|
+
:param console_close: Register the console close handler.
|
|
108
|
+
:param kill_signal: Register the kill signal handler.
|
|
109
|
+
"""
|
|
110
|
+
if at_exit:
|
|
111
|
+
atexit.register(self.atexit_handler)
|
|
112
|
+
if console_close:
|
|
113
|
+
win32api.SetConsoleCtrlHandler(self.console_handler, True)
|
|
114
|
+
if kill_signal:
|
|
115
|
+
signal.signal(signal.SIGINT, self.signal_handler)
|
|
116
|
+
signal.signal(signal.SIGTERM, self.signal_handler)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def register_exit_handler(
|
|
120
|
+
clean_up_function,
|
|
121
|
+
at_exit: bool = True,
|
|
122
|
+
console_close: bool = True,
|
|
123
|
+
kill_signal: bool = True,
|
|
124
|
+
*args, **kwargs):
|
|
102
125
|
"""
|
|
103
126
|
This function will register the exit handler to handle exit events: Closing the console, pressing 'CTRL+C',
|
|
104
127
|
Killing the process.
|
|
105
128
|
|
|
106
129
|
:param clean_up_function: The action to run when one of exit types is triggered.
|
|
130
|
+
:param at_exit: Register the atexit handler.
|
|
131
|
+
Just remember that the atexit handler will be called right away on [Ctrl+C], meaning if you want to do something
|
|
132
|
+
specifically on KeyboardInterrupt, you should handle it separately and set this parameter to False.
|
|
133
|
+
Same goes for all the exceptions.
|
|
134
|
+
:param console_close: Register the console close handler.
|
|
135
|
+
:param kill_signal: Register the kill signal handler.
|
|
136
|
+
Same problem as with atexit handler, it will be called right away on [Ctrl+C].
|
|
107
137
|
:param args: The arguments to pass to the cleanup action.
|
|
108
138
|
:param kwargs: The keyword arguments to pass to the cleanup action.
|
|
109
139
|
"""
|
|
110
140
|
global EXIT_HANDLER_INSTANCE
|
|
111
141
|
EXIT_HANDLER_INSTANCE = ExitHandler(clean_up_function, args, kwargs)
|
|
112
|
-
EXIT_HANDLER_INSTANCE.register_handlers()
|
|
142
|
+
EXIT_HANDLER_INSTANCE.register_handlers(at_exit=at_exit, console_close=console_close, kill_signal=kill_signal)
|
|
113
143
|
|
|
114
144
|
|
|
115
145
|
def restart_function(callable_function, *args, **kwargs):
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Loading resources using stdlib importlib.resources APIs (Python 3.7+)
|
|
2
|
+
https://docs.python.org/3/library/importlib.html#module-importlib.resources"""
|
|
3
|
+
import importlib.resources
|
|
4
|
+
from contextlib import redirect_stdout
|
|
5
|
+
import io
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Callable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PackageMainsProcessor:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
script_file_stem: str = None
|
|
15
|
+
):
|
|
16
|
+
self.script_file_stem: str = script_file_stem
|
|
17
|
+
self.resources_directory_name: str = 'a_mains'
|
|
18
|
+
|
|
19
|
+
def get_resource_path(self) -> str:
|
|
20
|
+
return f'{__package__}.{self.resources_directory_name}'
|
|
21
|
+
|
|
22
|
+
def read_script_file_to_string(self) -> str:
|
|
23
|
+
script_string = importlib.resources.read_text(self.get_resource_path(), f'{self.script_file_stem}.py')
|
|
24
|
+
|
|
25
|
+
return script_string
|
|
26
|
+
|
|
27
|
+
def execute_script_file(
|
|
28
|
+
self,
|
|
29
|
+
function_name: str = 'main',
|
|
30
|
+
args: tuple = None,
|
|
31
|
+
kwargs: dict = None,
|
|
32
|
+
get_printed_output: bool = False
|
|
33
|
+
) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Execute a script file from the package resources and get result as string.
|
|
36
|
+
|
|
37
|
+
:param function_name: Name of the function to call within the script.
|
|
38
|
+
:param args: Tuple of positional arguments to pass to the function.
|
|
39
|
+
:param kwargs: Dictionary of keyword arguments to pass to the function.
|
|
40
|
+
:param get_printed_output: If True, captures and returns printed output instead of return value.
|
|
41
|
+
|
|
42
|
+
:return: Output of the script execution as a string.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
if not args:
|
|
46
|
+
args = ()
|
|
47
|
+
if not kwargs:
|
|
48
|
+
kwargs = {}
|
|
49
|
+
|
|
50
|
+
module_name = f"{self.get_resource_path()}.{self.script_file_stem}" # script_file_name WITHOUT ".py"
|
|
51
|
+
|
|
52
|
+
module = importlib.import_module(module_name)
|
|
53
|
+
callable_function: Callable = getattr(module, function_name)
|
|
54
|
+
|
|
55
|
+
if get_printed_output:
|
|
56
|
+
with io.StringIO() as buffer, redirect_stdout(buffer):
|
|
57
|
+
callable_function(*args, **kwargs)
|
|
58
|
+
result = buffer.getvalue()
|
|
59
|
+
else:
|
|
60
|
+
result = callable_function(*args, **kwargs)
|
|
61
|
+
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
def execute_script_with_subprocess(
|
|
65
|
+
self,
|
|
66
|
+
arguments: list = None
|
|
67
|
+
) -> tuple[str, str, int]:
|
|
68
|
+
"""
|
|
69
|
+
Execute a script file from the package resources using subprocess and get result as string.
|
|
70
|
+
:param arguments: Dictionary of arguments to pass to the script.
|
|
71
|
+
Example: ['--port', '8080', '-v']
|
|
72
|
+
:return: Tuple containing (stdout, stderr, returncode).
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
# script_file_name WITHOUT ".py"
|
|
76
|
+
module_name = f"{self.get_resource_path()}.{self.script_file_stem}"
|
|
77
|
+
|
|
78
|
+
command = [sys.executable, "-m", module_name]
|
|
79
|
+
if arguments:
|
|
80
|
+
command.extend(arguments)
|
|
81
|
+
|
|
82
|
+
result = subprocess.run(command, capture_output=True, text=True)
|
|
83
|
+
|
|
84
|
+
return result.stdout, result.stderr, result.returncode
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import ctypes
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def is_admin() -> bool:
|
|
6
|
+
"""
|
|
7
|
+
Function checks on Windows or POSIX OSes if the script is executed under Administrative Privileges.
|
|
8
|
+
:return: True / False.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
if os.name == 'nt':
|
|
12
|
+
if ctypes.windll.shell32.IsUserAnAdmin() == 0:
|
|
13
|
+
result = False
|
|
14
|
+
else:
|
|
15
|
+
result = True
|
|
16
|
+
else:
|
|
17
|
+
if 'SUDO_USER' in os.environ and os.geteuid() == 0:
|
|
18
|
+
result = True
|
|
19
|
+
else:
|
|
20
|
+
result = False
|
|
21
|
+
|
|
22
|
+
return result
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import stat
|
|
3
|
+
import contextlib
|
|
4
|
+
import subprocess
|
|
5
|
+
import getpass
|
|
6
|
+
|
|
7
|
+
# Import pwd only on linux.
|
|
8
|
+
if os.name == 'posix':
|
|
9
|
+
import pwd
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_sudo_executer_username() -> str:
|
|
13
|
+
"""
|
|
14
|
+
Function gets the username of the user who executed the script with sudo.
|
|
15
|
+
:return: str, username.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
if 'SUDO_USER' in os.environ:
|
|
19
|
+
return os.environ['SUDO_USER']
|
|
20
|
+
else:
|
|
21
|
+
return ''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def detect_current_user(
|
|
25
|
+
optional_env_user_var: str = 'CUSTOM_SCRIPTED_USER'
|
|
26
|
+
) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Try to robustly determine the 'real' installing user.
|
|
29
|
+
|
|
30
|
+
Priority:
|
|
31
|
+
1. FDB_INSTALL_USER env var (explicit override).
|
|
32
|
+
2. If running as root with sudo: use SUDO_USER.
|
|
33
|
+
3. Otherwise: use effective uid.
|
|
34
|
+
4. Fallbacks: getpass.getuser() / $USER.
|
|
35
|
+
|
|
36
|
+
:param optional_env_user_var: str, name of the environment variable that can override the user detection.
|
|
37
|
+
:return: str, username.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# 1. Explicit override for weird environments (CI, containers, etc.)
|
|
41
|
+
env_user = os.getenv(optional_env_user_var)
|
|
42
|
+
if env_user:
|
|
43
|
+
return env_user
|
|
44
|
+
|
|
45
|
+
# 2. If we are root, prefer the sudo caller if any
|
|
46
|
+
try:
|
|
47
|
+
euid = os.geteuid()
|
|
48
|
+
except AttributeError: # non-POSIX, very unlikely here
|
|
49
|
+
euid = None
|
|
50
|
+
|
|
51
|
+
if euid == 0:
|
|
52
|
+
sudo_user = os.environ.get("SUDO_USER")
|
|
53
|
+
if sudo_user:
|
|
54
|
+
return sudo_user
|
|
55
|
+
|
|
56
|
+
# 3. Normal case: effective uid -> username
|
|
57
|
+
if euid is not None:
|
|
58
|
+
try:
|
|
59
|
+
return pwd.getpwuid(euid).pw_name
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
# 4. Fallbacks that don’t depend on utmp/tty
|
|
64
|
+
try:
|
|
65
|
+
return getpass.getuser()
|
|
66
|
+
except Exception:
|
|
67
|
+
return os.environ.get("USER", "unknown")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def set_executable(file_path: str):
|
|
71
|
+
"""
|
|
72
|
+
Function sets the executable permission on a file.
|
|
73
|
+
Equivalent to: chmod +x <file_path>
|
|
74
|
+
|
|
75
|
+
:param file_path: str, path to the file.
|
|
76
|
+
:return:
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# os.chmod(file_path, os.stat(file_path).st_mode | 0o111)
|
|
80
|
+
os.chmod(file_path, os.stat(file_path).st_mode | stat.S_IXUSR)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def set_trusted_executable(file_path: str):
|
|
84
|
+
"""
|
|
85
|
+
Function sets the executable permission on a file and marks it as trusted.
|
|
86
|
+
:param file_path: str, path to the file.
|
|
87
|
+
:return:
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
# Check if the file exists
|
|
91
|
+
if not os.path.exists(file_path):
|
|
92
|
+
raise FileNotFoundError(f"The file does not exist: {file_path} ")
|
|
93
|
+
|
|
94
|
+
# Execute the `gio set` command
|
|
95
|
+
subprocess.run(
|
|
96
|
+
["gio", "set", file_path, "metadata::trusted", "true"],
|
|
97
|
+
check=True
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def set_xfce_exe_checksum(desktop_file_path):
|
|
102
|
+
# Expand `~` to the full home directory path
|
|
103
|
+
desktop_file_path = os.path.expanduser(desktop_file_path)
|
|
104
|
+
|
|
105
|
+
# Ensure the file exists
|
|
106
|
+
if not os.path.exists(desktop_file_path):
|
|
107
|
+
raise FileNotFoundError(f"The file does not exist: {desktop_file_path} ")
|
|
108
|
+
|
|
109
|
+
# Calculate the SHA256 checksum of the file
|
|
110
|
+
result = subprocess.run(
|
|
111
|
+
["sha256sum", desktop_file_path],
|
|
112
|
+
stdout=subprocess.PIPE,
|
|
113
|
+
check=True,
|
|
114
|
+
text=True
|
|
115
|
+
)
|
|
116
|
+
checksum = result.stdout.split()[0]
|
|
117
|
+
|
|
118
|
+
# Set the metadata::xfce-exe-checksum attribute using `gio`
|
|
119
|
+
subprocess.run(
|
|
120
|
+
["gio", "set", "-t", "string", desktop_file_path, "metadata::xfce-exe-checksum", checksum],
|
|
121
|
+
check=True
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def change_file_owner(file_path: str, username: str):
|
|
126
|
+
"""
|
|
127
|
+
Function changes the owner of the file to the specified user.
|
|
128
|
+
:param file_path: str, path to the file.
|
|
129
|
+
:param username: str, username of the new owner.
|
|
130
|
+
:return:
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
uid = pwd.getpwnam(username).pw_uid
|
|
134
|
+
os.chown(file_path, uid, -1)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def is_executable(file_path: str) -> bool:
|
|
138
|
+
"""
|
|
139
|
+
Function checks if the file has the executable permission.
|
|
140
|
+
Equivalent to: stat -c "%a %n" <file_path>
|
|
141
|
+
|
|
142
|
+
:param file_path: str, path to the file.
|
|
143
|
+
:return: bool, True / False.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
return bool(os.stat(file_path).st_mode & stat.S_IXUSR)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def run_as_root(command):
|
|
150
|
+
subprocess.check_call(['sudo'] + command)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@contextlib.contextmanager
|
|
154
|
+
def temporary_regular_permissions():
|
|
155
|
+
"""
|
|
156
|
+
This function is used to temporarily change the effective user and group ID to the original user's.
|
|
157
|
+
This is used to run commands with the original user's permissions.
|
|
158
|
+
If you executed a script with 'sudo' and wanted certain action to execute as regular user and not root.
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
with temporary_regular_permissions():
|
|
162
|
+
# Do something with regular permissions.
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
:return:
|
|
166
|
+
"""
|
|
167
|
+
# Save the current effective user and group ID
|
|
168
|
+
original_euid, original_egid = os.geteuid(), os.getegid()
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
# Get the original user's UID and GID
|
|
172
|
+
orig_uid = int(os.environ.get('SUDO_UID', os.getuid()))
|
|
173
|
+
orig_gid = int(os.environ.get('SUDO_GID', os.getgid()))
|
|
174
|
+
|
|
175
|
+
# Set the effective user and group ID to the original user's
|
|
176
|
+
os.setegid(orig_gid)
|
|
177
|
+
os.seteuid(orig_uid)
|
|
178
|
+
|
|
179
|
+
# Provide the context to do something with these permissions
|
|
180
|
+
yield
|
|
181
|
+
finally:
|
|
182
|
+
# Revert to the original effective user and group ID
|
|
183
|
+
os.seteuid(original_euid)
|
|
184
|
+
os.setegid(original_egid)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def expand_user_path(user_name, path):
|
|
188
|
+
pwnam = pwd.getpwnam(user_name)
|
|
189
|
+
home_dir = pwnam.pw_dir
|
|
190
|
+
return path.replace("~", home_dir)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def set_folder_permissions(
|
|
194
|
+
folder_path: str,
|
|
195
|
+
username: str = None,
|
|
196
|
+
logged_in_non_sudo_user: bool = False
|
|
197
|
+
):
|
|
198
|
+
"""
|
|
199
|
+
Set ownership and permissions for an existing folder.
|
|
200
|
+
|
|
201
|
+
:param folder_path: Path to the folder (must already exist)
|
|
202
|
+
:param username: Username to assign ownership to (ignored if non_sudo_user=True)
|
|
203
|
+
:param logged_in_non_sudo_user: If True, use the current logged-in user unless running under sudo
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
if not username and not logged_in_non_sudo_user:
|
|
207
|
+
raise ValueError("A username must be provided, or 'non_sudo_user' must be set to True.")
|
|
208
|
+
|
|
209
|
+
# Handle non_sudo_user case
|
|
210
|
+
if logged_in_non_sudo_user:
|
|
211
|
+
# Get the current logged-in user
|
|
212
|
+
username = pwd.getpwuid(os.getuid())[0]
|
|
213
|
+
|
|
214
|
+
# Get the UID and GID of the specified user
|
|
215
|
+
user_info = pwd.getpwnam(username)
|
|
216
|
+
user_uid = user_info.pw_uid
|
|
217
|
+
user_gid = user_info.pw_gid
|
|
218
|
+
|
|
219
|
+
# Change ownership of the folder to the specified user
|
|
220
|
+
# print(f"Changing ownership of {folder_path} to user '{username}'...")
|
|
221
|
+
os.chown(folder_path, user_uid, user_gid)
|
|
222
|
+
|
|
223
|
+
# Set appropriate permissions (read, write, execute for the owner)
|
|
224
|
+
# print(f"Setting permissions for {folder_path}...")
|
|
225
|
+
os.chmod(folder_path, 0o755) # Owner rwx, group r-x, others r-x
|
|
226
|
+
|
|
227
|
+
# print(f"Ownership and permissions updated for folder: '{folder_path}'")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def is_directory_owner(directory_path: str, username: str) -> bool:
|
|
231
|
+
"""
|
|
232
|
+
Function checks if the directory is owned by the specified user.
|
|
233
|
+
:param directory_path: str, path to the directory.
|
|
234
|
+
:param username: str, username of the user.
|
|
235
|
+
:return: bool, True / False.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
uid = pwd.getpwnam(username).pw_uid
|
|
239
|
+
return os.stat(directory_path).st_uid == uid
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def unblock_file_windows(file_path):
|
|
5
|
+
"""
|
|
6
|
+
Unblock a file on Windows. This is used to unblock files downloaded from the internet.
|
|
7
|
+
When you Right-click then navigate to Properties, you will see the Unblock checkbox.
|
|
8
|
+
:param file_path:
|
|
9
|
+
:return:
|
|
10
|
+
"""
|
|
11
|
+
try:
|
|
12
|
+
subprocess.run(["powershell", "-Command", f"Unblock-File -Path '{file_path}'"], check=True)
|
|
13
|
+
print(f"Successfully unblocked the file: {file_path}")
|
|
14
|
+
except subprocess.CalledProcessError as e:
|
|
15
|
+
print(f"Failed to unblock the file: {file_path}\nError: {e}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_command_to_run_as_admin_windows(command: str) -> str:
|
|
19
|
+
"""
|
|
20
|
+
Function returns the command to run a command as administrator on Windows.
|
|
21
|
+
NOTE: When you run something this way, the parent will be the powershell.exe process.
|
|
22
|
+
If you need a status result directly of the executed command, you need to use subprocess.run() instead.
|
|
23
|
+
|
|
24
|
+
:param command: str, command to run.
|
|
25
|
+
:return: str, command to run as administrator.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
executable = command.split()[0]
|
|
29
|
+
command = (
|
|
30
|
+
f"powershell -Command "
|
|
31
|
+
f"\"Start-Process {executable} -ArgumentList '{' '.join(command.split()[1:])}' -Verb RunAs\"")
|
|
32
|
+
|
|
33
|
+
return command
|
atomicshop/print_api.py
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import sys
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any
|
|
2
4
|
|
|
3
|
-
from .basics
|
|
5
|
+
from .basics import ansi_escape_codes
|
|
4
6
|
from .basics import tracebacks
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
# noinspection PyUnusedLocal,PyIncorrectDocstring
|
|
8
9
|
def print_api(
|
|
9
|
-
message:
|
|
10
|
-
color:
|
|
10
|
+
message: Any,
|
|
11
|
+
color: Any = None,
|
|
11
12
|
print_end: str = '\n',
|
|
12
13
|
rtl: bool = False,
|
|
13
14
|
error_type: bool = False,
|
|
14
|
-
logger:
|
|
15
|
+
logger: logging.Logger = None,
|
|
15
16
|
logger_method: str = 'info',
|
|
16
17
|
stdout: bool = True,
|
|
17
18
|
stderr: bool = True,
|
|
@@ -19,8 +20,6 @@ def print_api(
|
|
|
19
20
|
traceback_string: bool = False,
|
|
20
21
|
oneline: bool = False,
|
|
21
22
|
oneline_end: str = '',
|
|
22
|
-
# raise_exception: bool = True,
|
|
23
|
-
# exit_on_error: bool = False,
|
|
24
23
|
**kwargs: object) -> None:
|
|
25
24
|
"""
|
|
26
25
|
Function of custom api that is responsible for printing messages to console.
|
|
@@ -82,18 +81,26 @@ def print_api(
|
|
|
82
81
|
|
|
83
82
|
# This section takes care of different types of string manipulations for message.
|
|
84
83
|
|
|
85
|
-
# If 'exit_on_error' is set to 'True', we'll add 'exit_message' on new line after 'message'.
|
|
86
|
-
# if error_type and exit_on_error and raise_exception:
|
|
87
|
-
# message = message + '\n' + exit_message
|
|
88
|
-
|
|
89
84
|
# If 'rtl' is set to 'True', we'll add Right-To-Left text conversion to 'message'.
|
|
90
85
|
if rtl:
|
|
91
|
-
# Lazy importing of 'bidi' library. It's not a problem since python caches the library after first import.
|
|
92
|
-
# Off-course, it will be imported from the cache each time this section is triggered.
|
|
93
86
|
# pip install python-bidi
|
|
94
87
|
from bidi.algorithm import get_display
|
|
95
88
|
message = get_display(message)
|
|
96
89
|
|
|
90
|
+
if logger_method == 'error' or logger_method == 'critical':
|
|
91
|
+
error_type = True
|
|
92
|
+
|
|
93
|
+
# If exception was raised and 'stderr=True'.
|
|
94
|
+
if sys.exc_info()[0] is not None and stderr and traceback_string:
|
|
95
|
+
# If 'traceback' is set to 'True', we'll output traceback of exception.
|
|
96
|
+
if traceback_string:
|
|
97
|
+
if message:
|
|
98
|
+
message = f'{message}\n{tracebacks.get_as_string()}{message}'
|
|
99
|
+
else:
|
|
100
|
+
message = tracebacks.get_as_string()
|
|
101
|
+
|
|
102
|
+
color = 'red'
|
|
103
|
+
|
|
97
104
|
# If 'stdcolor' is 'True', the console output will be colored.
|
|
98
105
|
if stdcolor:
|
|
99
106
|
# If 'logger.error' should be outputted to console, and 'color' wasn't selected, then set color to 'yellow'.
|
|
@@ -103,19 +110,8 @@ def print_api(
|
|
|
103
110
|
elif logger_method == 'critical' and not color:
|
|
104
111
|
color = 'red'
|
|
105
112
|
|
|
106
|
-
if color:
|
|
107
|
-
message = get_colors_basic_dict(color) + message + ColorsBasic.END
|
|
108
|
-
|
|
109
|
-
if logger_method == 'error' or logger_method == 'critical':
|
|
110
|
-
error_type = True
|
|
111
|
-
|
|
112
|
-
# If exception was raised and 'stderr=True'.
|
|
113
|
-
if sys.exc_info()[0] is not None and stderr:
|
|
114
|
-
# If 'traceback' is set to 'True', we'll output traceback of exception.
|
|
115
|
-
if traceback_string:
|
|
116
|
-
message = f'{message} | Exception: {tracebacks.get_as_string()}'
|
|
117
|
-
|
|
118
|
-
color = 'red'
|
|
113
|
+
if color is not None:
|
|
114
|
+
message = ansi_escape_codes.get_colors_basic_dict(color) + message + ansi_escape_codes.ColorsBasic.END
|
|
119
115
|
|
|
120
116
|
# If 'online' is set to 'True', we'll output message as oneline.
|
|
121
117
|
if oneline:
|
|
@@ -130,14 +126,14 @@ def print_api(
|
|
|
130
126
|
if print_end == '\n':
|
|
131
127
|
# Use logger to output message.
|
|
132
128
|
getattr(logger, logger_method)(message)
|
|
129
|
+
else:
|
|
130
|
+
raise ValueError("Logger can't output messages with 'print_end' other than '\\n'.")
|
|
133
131
|
# If logger wasn't passed.
|
|
134
132
|
else:
|
|
135
133
|
# Use print to output the message.
|
|
136
134
|
print(message, end=print_end)
|
|
137
135
|
|
|
138
136
|
# = Main Section with printing cases ===============================================================================
|
|
139
|
-
# exit_message: str = 'Exiting...'
|
|
140
|
-
|
|
141
137
|
# Convert message to string.
|
|
142
138
|
message = str(message)
|
|
143
139
|
|
|
@@ -151,20 +147,6 @@ def print_api(
|
|
|
151
147
|
if error_type:
|
|
152
148
|
print_or_logger()
|
|
153
149
|
|
|
154
|
-
# ==================================
|
|
155
|
-
# This section is responsible for ending the script.
|
|
156
|
-
|
|
157
|
-
# Check if we're inside exception. In this case each of 3 entries in 'sys.exc_info()' tuple will not equal
|
|
158
|
-
# to 'None', so picked only the first one.
|
|
159
|
-
# if sys.exc_info()[0] and not exit_on_error:
|
|
160
|
-
# # If 'raise_exception' is set to 'True', we'll end the script with exception.
|
|
161
|
-
# if pass_exception:
|
|
162
|
-
# pass
|
|
163
|
-
|
|
164
|
-
# If 'exit_on_error' is set to 'True', we'll end the script.
|
|
165
|
-
# if exit_on_error and error_type:
|
|
166
|
-
# sys.exit()
|
|
167
|
-
|
|
168
150
|
|
|
169
151
|
def print_status(
|
|
170
152
|
prefix_string: str,
|
atomicshop/process.py
CHANGED
|
@@ -82,7 +82,18 @@ def execute_with_live_output(
|
|
|
82
82
|
:return: Boolean, If execution was successful, return True, if not - False.
|
|
83
83
|
"""
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
if isinstance(cmd, str):
|
|
86
|
+
shell = True
|
|
87
|
+
elif isinstance(cmd, list):
|
|
88
|
+
shell = False
|
|
89
|
+
else:
|
|
90
|
+
raise TypeError(f'cmd must be a string or list, not {type(cmd)}')
|
|
91
|
+
|
|
92
|
+
if wsl:
|
|
93
|
+
if isinstance(cmd, str):
|
|
94
|
+
cmd = 'wsl ' + cmd
|
|
95
|
+
elif isinstance(cmd, list):
|
|
96
|
+
cmd = ['wsl'] + cmd
|
|
86
97
|
|
|
87
98
|
# Needed imports:
|
|
88
99
|
# from subprocess import Popen, PIPE, STDOUT
|
|
@@ -103,7 +114,7 @@ def execute_with_live_output(
|
|
|
103
114
|
# The buffer size is system-dependent and usually chosen by the underlying implementation to optimize performance.
|
|
104
115
|
# # bufsize=0: This means no buffering.
|
|
105
116
|
# The I/O is unbuffered, and data is written or read from the stream immediately.
|
|
106
|
-
with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True) as process:
|
|
117
|
+
with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True, shell=shell) as process:
|
|
107
118
|
# We'll count the number of lines from 'process.stdout'.
|
|
108
119
|
counter: int = 0
|
|
109
120
|
# And also get list of all the lines.
|
|
@@ -305,14 +316,21 @@ def kill_process_by_filename_pattern(pattern: str):
|
|
|
305
316
|
processes.kill_process_by_pid(running_processes[0]['pid'])
|
|
306
317
|
|
|
307
318
|
|
|
308
|
-
def run_powershell_command(
|
|
319
|
+
def run_powershell_command(
|
|
320
|
+
command: str
|
|
321
|
+
):
|
|
309
322
|
try:
|
|
310
323
|
result = subprocess.run(["powershell", "-Command", command], capture_output=True, text=True, check=True)
|
|
311
|
-
|
|
324
|
+
if result.stdout:
|
|
325
|
+
print_api(result.stdout)
|
|
326
|
+
if result.stderr: # PS can write warnings to stderr even on success
|
|
327
|
+
print_api(result.stderr, color='yellow')
|
|
312
328
|
return result.stdout
|
|
313
329
|
except subprocess.CalledProcessError as e:
|
|
314
|
-
|
|
315
|
-
|
|
330
|
+
# e.stderr and e.stdout are populated because capture_output=True
|
|
331
|
+
msg = (e.stderr or e.stdout or f"PowerShell exited with code {e.returncode}")
|
|
332
|
+
print_api(msg, color='red', error_type=True)
|
|
333
|
+
return msg
|
|
316
334
|
|
|
317
335
|
|
|
318
336
|
"""
|
|
@@ -3,7 +3,6 @@ import multiprocessing
|
|
|
3
3
|
import time
|
|
4
4
|
from typing import Literal, Union
|
|
5
5
|
|
|
6
|
-
from ..print_api import print_api
|
|
7
6
|
from .tracers import sysmon_etw, event_log
|
|
8
7
|
from .pollers import psutil_pywin32wmi_dll
|
|
9
8
|
from ..wrappers.pywin32w.win_event_log.subscribes import process_terminate
|