atomicshop 2.11.47__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/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- 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/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- 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/appointment_management.py +5 -3
- 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/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- 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 -74
- 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 +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- 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 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- 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/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- 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 +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- 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/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- 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 +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- 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 +500 -173
- 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 +14 -9
- 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 +157 -0
- 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.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- 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/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/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- 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/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- 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/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /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/mains → 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/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
|
@@ -1,143 +1,18 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import datetime
|
|
3
|
+
import json
|
|
4
|
+
from typing import Union, Literal
|
|
5
|
+
import argparse
|
|
2
6
|
|
|
7
|
+
from .statistic_analyzer_helper import analyzer_helper, moving_average_helper
|
|
3
8
|
from .. import filesystem, domains, datetimes, urls
|
|
4
9
|
from ..basics import dicts
|
|
5
|
-
from ..file_io import tomls, xlsxs
|
|
10
|
+
from ..file_io import tomls, xlsxs, jsons, csvs
|
|
6
11
|
from ..wrappers.loggingw import reading
|
|
7
12
|
from ..print_api import print_api
|
|
8
13
|
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
"""
|
|
12
|
-
This function gets the last day number from the statistics content.
|
|
13
|
-
|
|
14
|
-
:param statistics_content: list, of lines in the statistics content.
|
|
15
|
-
:param stop_after_lines: integer, if specified, the function will stop after the specified number of lines.
|
|
16
|
-
:return: integer, the last day number.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
last_day_number = None
|
|
20
|
-
start_time_temp = None
|
|
21
|
-
for line_index, line in enumerate(statistics_content):
|
|
22
|
-
try:
|
|
23
|
-
request_time = datetime.datetime.strptime(line['request_time_sent'], '%Y-%m-%d %H:%M:%S.%f')
|
|
24
|
-
except ValueError:
|
|
25
|
-
continue
|
|
26
|
-
|
|
27
|
-
if not start_time_temp:
|
|
28
|
-
start_time_temp = request_time
|
|
29
|
-
|
|
30
|
-
if stop_after_lines:
|
|
31
|
-
if line_index == stop_after_lines:
|
|
32
|
-
break
|
|
33
|
-
|
|
34
|
-
last_day_number = datetimes.get_difference_between_dates_in_days(start_time_temp, request_time)
|
|
35
|
-
return last_day_number
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def create_empty_features_dict() -> dict:
|
|
39
|
-
"""
|
|
40
|
-
This function creates an empty dictionary for the daily stats. This should be initiated for each 'host_type' of:
|
|
41
|
-
'domain', 'subdomain', 'url_no_parameters'.
|
|
42
|
-
:return: dict
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
'total_count': {}, 'normal_count': {}, 'error_count': {},
|
|
47
|
-
'request_0_byte_count': {}, 'response_0_byte_count': {},
|
|
48
|
-
'request_sizes_list': {}, 'response_sizes_list': {},
|
|
49
|
-
'request_sizes_no_0_bytes_list': {}, 'response_sizes_no_0_bytes_list': {},
|
|
50
|
-
'average_request_size': {}, 'average_response_size': {},
|
|
51
|
-
'average_request_size_no_0_bytes': {}, 'average_response_size_no_0_bytes': {}}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def add_to_count_to_daily_stats(
|
|
55
|
-
daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str) -> None:
|
|
56
|
-
"""
|
|
57
|
-
This function adds 1 to the 'count' feature of the current day in the daily stats.
|
|
58
|
-
|
|
59
|
-
:param daily_stats: dict, the daily statistics dict.
|
|
60
|
-
:param current_day: integer, the current day number.
|
|
61
|
-
:param last_day: integer, the last day number.
|
|
62
|
-
:param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
|
|
63
|
-
:param feature: string, the feature to add the count to. Can be: 'total_count', 'normal_count', 'error_count',
|
|
64
|
-
'request_0_byte_count', 'response_0_byte_count'.
|
|
65
|
-
:param host_name: string, the name of the host.
|
|
66
|
-
|
|
67
|
-
:return: None.
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
# Aggregate daily domain hits.
|
|
71
|
-
if host_name not in daily_stats[host_type][feature].keys():
|
|
72
|
-
daily_stats[host_type][feature][host_name] = {}
|
|
73
|
-
# Iterate from first day to the last day.
|
|
74
|
-
for day in range(0, last_day + 1):
|
|
75
|
-
daily_stats[host_type][feature][host_name][day] = 0
|
|
76
|
-
|
|
77
|
-
# Add count to current day.
|
|
78
|
-
daily_stats[host_type][feature][host_name][current_day] += 1
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def add_to_list_to_daily_stats(
|
|
82
|
-
daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str,
|
|
83
|
-
size: float) -> None:
|
|
84
|
-
"""
|
|
85
|
-
This function adds the 'size' to the 'feature' list of the current day in the daily stats.
|
|
86
|
-
|
|
87
|
-
:param daily_stats: dict, the daily statistics dict.
|
|
88
|
-
:param current_day: integer, the current day number.
|
|
89
|
-
:param last_day: integer, the last day number.
|
|
90
|
-
:param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
|
|
91
|
-
:param feature: string, the feature to add the count to. Can be: 'request_sizes_list', 'response_sizes_list',
|
|
92
|
-
'request_sizes_no_0_bytes_list', 'response_sizes_no_0_bytes_list'.
|
|
93
|
-
:param host_name: string, the name of the host.
|
|
94
|
-
:param size: float, the size in bytes to add to the list.
|
|
95
|
-
|
|
96
|
-
:return: None.
|
|
97
|
-
"""
|
|
98
|
-
|
|
99
|
-
# Aggregate daily domain hits.
|
|
100
|
-
if host_name not in daily_stats[host_type][feature].keys():
|
|
101
|
-
daily_stats[host_type][feature][host_name] = {}
|
|
102
|
-
# Iterate from first day to the last day.
|
|
103
|
-
for day in range(0, last_day + 1):
|
|
104
|
-
daily_stats[host_type][feature][host_name][day] = []
|
|
105
|
-
|
|
106
|
-
# Add count to current day.
|
|
107
|
-
daily_stats[host_type][feature][host_name][current_day].append(size)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def add_to_average_to_daily_stats(
|
|
111
|
-
daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str,
|
|
112
|
-
list_of_sizes: list) -> None:
|
|
113
|
-
"""
|
|
114
|
-
This function adds the average size in bytes calculated from the 'list_of_sizes' to the 'feature' of the current
|
|
115
|
-
day in the daily stats.
|
|
116
|
-
|
|
117
|
-
:param daily_stats: dict, the daily statistics dict.
|
|
118
|
-
:param current_day: integer, the current day number.
|
|
119
|
-
:param last_day: integer, the last day number.
|
|
120
|
-
:param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
|
|
121
|
-
:param feature: string, the feature to add the count to. Can be: 'average_request_size', 'average_response_size',
|
|
122
|
-
'average_request_size_no_0_bytes', 'average_response_size_no_0_bytes'.
|
|
123
|
-
:param host_name: string, the name of the host.
|
|
124
|
-
:param list_of_sizes: list, the list of sizes to calculate the average from.
|
|
125
|
-
|
|
126
|
-
:return: None.
|
|
127
|
-
"""
|
|
128
|
-
|
|
129
|
-
# Aggregate daily domain hits.
|
|
130
|
-
if host_name not in daily_stats[host_type][feature].keys():
|
|
131
|
-
daily_stats[host_type][feature][host_name] = {}
|
|
132
|
-
# Iterate from first day to the last day.
|
|
133
|
-
for day in range(0, last_day + 1):
|
|
134
|
-
daily_stats[host_type][feature][host_name][day] = 0
|
|
135
|
-
|
|
136
|
-
# If the list of size is empty, add 0 to the average, since we cannot divide by 0.
|
|
137
|
-
if len(list_of_sizes) == 0:
|
|
138
|
-
daily_stats[host_type][feature][host_name][current_day] = 0
|
|
139
|
-
else:
|
|
140
|
-
daily_stats[host_type][feature][host_name][current_day] = sum(list_of_sizes) / len(list_of_sizes)
|
|
15
|
+
STATISTICS_FILE_NAME: str = 'statistics.csv'
|
|
141
16
|
|
|
142
17
|
|
|
143
18
|
def analyze(main_file_path: str):
|
|
@@ -154,8 +29,10 @@ def analyze(main_file_path: str):
|
|
|
154
29
|
summary_path: str = filesystem.check_absolute_path___add_full(config['report_file_path'], script_directory)
|
|
155
30
|
|
|
156
31
|
# Get the content from statistics files.
|
|
157
|
-
|
|
158
|
-
|
|
32
|
+
log_file_path_pattern: str = f"{config['statistic_files_path']}{os.sep}statistics.csv"
|
|
33
|
+
statistics_content: list = reading.get_all_log_files_into_list(
|
|
34
|
+
log_file_path=log_file_path_pattern,
|
|
35
|
+
log_type='csv'
|
|
159
36
|
)
|
|
160
37
|
|
|
161
38
|
# Initialize loop.
|
|
@@ -167,9 +44,9 @@ def analyze(main_file_path: str):
|
|
|
167
44
|
'subdomain': {'total_count': {}, 'normal_count': {}, 'error_count': {}}
|
|
168
45
|
}
|
|
169
46
|
daily_stats: dict = {
|
|
170
|
-
'domain': create_empty_features_dict(),
|
|
171
|
-
'subdomain': create_empty_features_dict(),
|
|
172
|
-
'url_no_parameters': create_empty_features_dict()
|
|
47
|
+
'domain': analyzer_helper.create_empty_features_dict(),
|
|
48
|
+
'subdomain': analyzer_helper.create_empty_features_dict(),
|
|
49
|
+
'url_no_parameters': analyzer_helper.create_empty_features_dict()
|
|
173
50
|
}
|
|
174
51
|
|
|
175
52
|
# Start the main loop.
|
|
@@ -190,7 +67,7 @@ def analyze(main_file_path: str):
|
|
|
190
67
|
|
|
191
68
|
# Find the last day number. If 'break_after_lines' is specified, the loop will stop after the specified line.
|
|
192
69
|
if not last_day_number:
|
|
193
|
-
last_day_number = get_the_last_day_number(statistics_content, break_after_lines)
|
|
70
|
+
last_day_number = analyzer_helper.get_the_last_day_number(statistics_content, break_after_lines)
|
|
194
71
|
|
|
195
72
|
if break_after_lines:
|
|
196
73
|
if line_index == break_after_lines:
|
|
@@ -290,87 +167,87 @@ def analyze(main_file_path: str):
|
|
|
290
167
|
day_number = datetimes.get_difference_between_dates_in_days(start_time, request_time)
|
|
291
168
|
|
|
292
169
|
# Add 1 to the total count of the current day.
|
|
293
|
-
add_to_count_to_daily_stats(
|
|
170
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
294
171
|
daily_stats, day_number, last_day_number, 'domain', 'total_count', main_domain)
|
|
295
|
-
add_to_count_to_daily_stats(
|
|
172
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
296
173
|
daily_stats, day_number, last_day_number, 'subdomain', 'total_count', subdomain)
|
|
297
|
-
add_to_count_to_daily_stats(
|
|
174
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
298
175
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'total_count', url_no_parameters)
|
|
299
176
|
|
|
300
177
|
# Handle line if it has error.
|
|
301
178
|
if line['error'] != '':
|
|
302
|
-
add_to_count_to_daily_stats(
|
|
179
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
303
180
|
daily_stats, day_number, last_day_number, 'domain', 'error_count', main_domain)
|
|
304
|
-
add_to_count_to_daily_stats(
|
|
181
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
305
182
|
daily_stats, day_number, last_day_number, 'subdomain', 'error_count', subdomain)
|
|
306
|
-
add_to_count_to_daily_stats(
|
|
183
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
307
184
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'error_count', url_no_parameters)
|
|
308
185
|
else:
|
|
309
|
-
add_to_count_to_daily_stats(
|
|
186
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
310
187
|
daily_stats, day_number, last_day_number, 'domain', 'normal_count', main_domain)
|
|
311
|
-
add_to_count_to_daily_stats(
|
|
188
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
312
189
|
daily_stats, day_number, last_day_number, 'subdomain', 'normal_count', subdomain)
|
|
313
|
-
add_to_count_to_daily_stats(
|
|
190
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
314
191
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'normal_count', url_no_parameters)
|
|
315
192
|
|
|
316
193
|
if request_size == 0:
|
|
317
|
-
add_to_count_to_daily_stats(
|
|
194
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
318
195
|
daily_stats, day_number, last_day_number, 'domain', 'request_0_byte_count',
|
|
319
196
|
main_domain)
|
|
320
|
-
add_to_count_to_daily_stats(
|
|
197
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
321
198
|
daily_stats, day_number, last_day_number, 'subdomain', 'request_0_byte_count',
|
|
322
199
|
subdomain)
|
|
323
|
-
add_to_count_to_daily_stats(
|
|
200
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
324
201
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'request_0_byte_count',
|
|
325
202
|
url_no_parameters)
|
|
326
203
|
|
|
327
204
|
if response_size == 0:
|
|
328
|
-
add_to_count_to_daily_stats(
|
|
205
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
329
206
|
daily_stats, day_number, last_day_number, 'domain', 'response_0_byte_count',
|
|
330
207
|
main_domain)
|
|
331
|
-
add_to_count_to_daily_stats(
|
|
208
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
332
209
|
daily_stats, day_number, last_day_number, 'subdomain', 'response_0_byte_count',
|
|
333
210
|
subdomain)
|
|
334
|
-
add_to_count_to_daily_stats(
|
|
211
|
+
analyzer_helper.add_to_count_to_daily_stats(
|
|
335
212
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'response_0_byte_count',
|
|
336
213
|
url_no_parameters)
|
|
337
214
|
|
|
338
215
|
if request_size is not None and response_size is not None:
|
|
339
|
-
add_to_list_to_daily_stats(
|
|
216
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
340
217
|
daily_stats, day_number, last_day_number, 'domain', 'request_sizes_list', main_domain, request_size)
|
|
341
|
-
add_to_list_to_daily_stats(
|
|
218
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
342
219
|
daily_stats, day_number, last_day_number, 'subdomain', 'request_sizes_list', subdomain, request_size)
|
|
343
|
-
add_to_list_to_daily_stats(
|
|
220
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
344
221
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'request_sizes_list', url_no_parameters,
|
|
345
222
|
request_size)
|
|
346
223
|
|
|
347
|
-
add_to_list_to_daily_stats(
|
|
224
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
348
225
|
daily_stats, day_number, last_day_number, 'domain', 'response_sizes_list', main_domain, response_size)
|
|
349
|
-
add_to_list_to_daily_stats(
|
|
226
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
350
227
|
daily_stats, day_number, last_day_number, 'subdomain', 'response_sizes_list', subdomain, response_size)
|
|
351
|
-
add_to_list_to_daily_stats(
|
|
228
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
352
229
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'response_sizes_list', url_no_parameters,
|
|
353
230
|
response_size)
|
|
354
231
|
|
|
355
232
|
if request_size != 0 and request_size is not None:
|
|
356
|
-
add_to_list_to_daily_stats(
|
|
233
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
357
234
|
daily_stats, day_number, last_day_number, 'domain', 'request_sizes_no_0_bytes_list',
|
|
358
235
|
main_domain, request_size)
|
|
359
|
-
add_to_list_to_daily_stats(
|
|
236
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
360
237
|
daily_stats, day_number, last_day_number, 'subdomain', 'request_sizes_no_0_bytes_list',
|
|
361
238
|
subdomain, request_size)
|
|
362
|
-
add_to_list_to_daily_stats(
|
|
239
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
363
240
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'request_sizes_no_0_bytes_list',
|
|
364
241
|
url_no_parameters, request_size)
|
|
365
242
|
|
|
366
243
|
if response_size != 0 and response_size is not None:
|
|
367
|
-
add_to_list_to_daily_stats(
|
|
244
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
368
245
|
daily_stats, day_number, last_day_number, 'domain', 'response_sizes_no_0_bytes_list',
|
|
369
246
|
main_domain, response_size)
|
|
370
|
-
add_to_list_to_daily_stats(
|
|
247
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
371
248
|
daily_stats, day_number, last_day_number, 'subdomain', 'response_sizes_no_0_bytes_list',
|
|
372
249
|
subdomain, response_size)
|
|
373
|
-
add_to_list_to_daily_stats(
|
|
250
|
+
analyzer_helper.add_to_list_to_daily_stats(
|
|
374
251
|
daily_stats, day_number, last_day_number, 'url_no_parameters', 'response_sizes_no_0_bytes_list',
|
|
375
252
|
url_no_parameters, response_size)
|
|
376
253
|
|
|
@@ -392,7 +269,7 @@ def analyze(main_file_path: str):
|
|
|
392
269
|
|
|
393
270
|
for host_name, days in hosts.items():
|
|
394
271
|
for day, sizes in days.items():
|
|
395
|
-
add_to_average_to_daily_stats(
|
|
272
|
+
analyzer_helper.add_to_average_to_daily_stats(
|
|
396
273
|
daily_stats, day, last_day_number, host_type, feature_name, host_name, sizes)
|
|
397
274
|
|
|
398
275
|
# Sorting overall stats.
|
|
@@ -463,3 +340,217 @@ def analyze(main_file_path: str):
|
|
|
463
340
|
xlsxs.write_xlsx(combined_sorted_stats, file_path=summary_path)
|
|
464
341
|
|
|
465
342
|
return
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# ======================================================================================================================
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def deviation_calculator_by_moving_average(
|
|
349
|
+
statistics_file_directory: str = None,
|
|
350
|
+
statistics_content: dict = None,
|
|
351
|
+
by_type: Literal['host', 'url'] = 'url',
|
|
352
|
+
moving_average_window_days: int = 5,
|
|
353
|
+
top_bottom_deviation_percentage: float = 0.25,
|
|
354
|
+
get_deviation_for_last_day_only: bool = False,
|
|
355
|
+
get_deviation_for_date: str = None,
|
|
356
|
+
summary: bool = False,
|
|
357
|
+
output_file_path: str = None,
|
|
358
|
+
output_file_type: Literal['json', 'csv'] = 'json',
|
|
359
|
+
convert_sizes_lists_and_ma_data_to_string: bool = False,
|
|
360
|
+
skip_total_count_less_than: int = None
|
|
361
|
+
) -> Union[list, None]:
|
|
362
|
+
"""
|
|
363
|
+
This function is the main function for the moving average calculator.
|
|
364
|
+
|
|
365
|
+
:param statistics_file_directory: string, can be either providing the directory where 'statistics.csv' file resides
|
|
366
|
+
or None. If None, the 'statistics_content' must be provided.
|
|
367
|
+
The directory where 'statistics.csv' file resides.
|
|
368
|
+
Also, all the rotated files like: statistics_2021-01-01.csv, statistics_2021-01-02.csv, etc.
|
|
369
|
+
These will be analyzed in the order of the date in the file name.
|
|
370
|
+
:param statistics_content: dict, if specified, this will be used instead of reading the files from the directory.
|
|
371
|
+
The dict should be a result of the 'atomicshop.mitm.statistic_analyzer_helper.moving_average_helper.get_all_files_content'.
|
|
372
|
+
:param by_type: string, 'host' or 'url'. The type of the deviation calculation.
|
|
373
|
+
'host' will calculate the deviation by the host name. Example: maps.google.com, yahoo.com, etc.
|
|
374
|
+
'url' will calculate the deviation by the URL. Example: maps.google.com/maps, yahoo.com/news, etc.
|
|
375
|
+
:param moving_average_window_days: integer, the moving average window days.
|
|
376
|
+
:param top_bottom_deviation_percentage: float, the top bottom deviation percentage. Example: 0.1 for 10%.
|
|
377
|
+
:param get_deviation_for_last_day_only: bool, if True, only the last day will be analyzed.
|
|
378
|
+
Example: With 'moving_average_window_days=5', the last 6 days will be analyzed.
|
|
379
|
+
5 days for moving average and the last day for deviation.
|
|
380
|
+
File names example:
|
|
381
|
+
statistics_2021-01-01.csv
|
|
382
|
+
statistics_2021-01-02.csv
|
|
383
|
+
statistics_2021-01-03.csv
|
|
384
|
+
statistics_2021-01-04.csv
|
|
385
|
+
statistics_2021-01-05.csv
|
|
386
|
+
statistics_2021-01-06.csv
|
|
387
|
+
Files 01 to 05 will be used for moving average and the file 06 for deviation.
|
|
388
|
+
Meaning the average calculated for 2021-01-06 will be compared to the values moving average of 2021-01-01
|
|
389
|
+
to 2021-01-05.
|
|
390
|
+
:param get_deviation_for_date: string, if specified, only the specified date will be analyzed.
|
|
391
|
+
The date must be in the format of 'YYYY-MM-DD'. Example: '2021-01-06'.
|
|
392
|
+
:param summary: bool, if True, Only the summary will be generated without all the numbers that were used
|
|
393
|
+
to calculate the averages and the moving average data.
|
|
394
|
+
:param output_file_path: string, if None, no file will be written.
|
|
395
|
+
:param output_file_type: string, the type of the output file. 'json' or 'csv'.
|
|
396
|
+
:param convert_sizes_lists_and_ma_data_to_string: bool, if True, the 'request_sizes', 'response_sizes' and 'ma_data'
|
|
397
|
+
will be converted to string. This is useful when writing to files, so the view will be more readable.
|
|
398
|
+
:param skip_total_count_less_than: integer, if specified, the deviation calculation will be skipped
|
|
399
|
+
if the total count is less than this number.
|
|
400
|
+
This means that if we have moving average window of 7 days, on the 8th day, the deviation will be calculated
|
|
401
|
+
only if the total count of the checked type
|
|
402
|
+
(request average size, response average, request count, response count)
|
|
403
|
+
is greater or equal to this number.
|
|
404
|
+
-----------------------------
|
|
405
|
+
:return: the deviation list of dicts.
|
|
406
|
+
|
|
407
|
+
Example:
|
|
408
|
+
import sys
|
|
409
|
+
from atomicshop.mitm import statistic_analyzer
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def main():
|
|
413
|
+
return statistic_analyzer.moving_average_calculator_main(
|
|
414
|
+
statistics_file_path='statistics.csv',
|
|
415
|
+
moving_average_window_days=7,
|
|
416
|
+
top_bottom_deviation_percentage=0.1,
|
|
417
|
+
output_json_file='C:\\output\\deviation_list.json'
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
if __name__ == '__main__':
|
|
422
|
+
sys.exit(main())
|
|
423
|
+
"""
|
|
424
|
+
|
|
425
|
+
def convert_data_value_to_string(value_key: str, list_index: int) -> None:
|
|
426
|
+
deviation_list[list_index]['data'][value_key] = json.dumps(deviation_list[list_index]['data'][value_key])
|
|
427
|
+
|
|
428
|
+
def convert_value_to_string(value_key: str, list_index: int) -> None:
|
|
429
|
+
if value_key in deviation_list[list_index]:
|
|
430
|
+
deviation_list[list_index][value_key] = json.dumps(deviation_list[list_index][value_key])
|
|
431
|
+
|
|
432
|
+
if not statistics_file_directory and not statistics_content:
|
|
433
|
+
raise ValueError('Either [statistics_file_directory] or [statistics_content] must be provided.')
|
|
434
|
+
if statistics_file_directory and statistics_content:
|
|
435
|
+
raise ValueError('Either [statistics_file_directory] or [statistics_content] must be provided, not both.')
|
|
436
|
+
|
|
437
|
+
if output_file_type not in ['json', 'csv']:
|
|
438
|
+
raise ValueError(f'output_file_type must be "json" or "csv", not [{output_file_type}]')
|
|
439
|
+
|
|
440
|
+
if by_type not in ['host', 'url']:
|
|
441
|
+
raise ValueError(f'by_type must be "host" or "url", not [{by_type}]')
|
|
442
|
+
|
|
443
|
+
if get_deviation_for_last_day_only and get_deviation_for_date:
|
|
444
|
+
raise ValueError('Either [get_deviation_for_last_day_only] or [get_deviation_for_date] can be provided, not both.')
|
|
445
|
+
|
|
446
|
+
if statistics_file_directory:
|
|
447
|
+
statistics_file_path: str | None = f'{statistics_file_directory}{os.sep}{STATISTICS_FILE_NAME}'
|
|
448
|
+
else:
|
|
449
|
+
statistics_file_path: str | None = None
|
|
450
|
+
|
|
451
|
+
deviation_list = moving_average_helper.calculate_moving_average(
|
|
452
|
+
statistics_file_path,
|
|
453
|
+
statistics_content,
|
|
454
|
+
by_type,
|
|
455
|
+
moving_average_window_days,
|
|
456
|
+
top_bottom_deviation_percentage,
|
|
457
|
+
get_deviation_for_last_day_only,
|
|
458
|
+
get_deviation_for_date,
|
|
459
|
+
skip_total_count_less_than
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
if deviation_list:
|
|
463
|
+
if convert_sizes_lists_and_ma_data_to_string:
|
|
464
|
+
for deviation_list_index, deviation in enumerate(deviation_list):
|
|
465
|
+
convert_data_value_to_string('request_sizes', deviation_list_index)
|
|
466
|
+
convert_data_value_to_string('response_sizes', deviation_list_index)
|
|
467
|
+
convert_value_to_string('ma_data', deviation_list_index)
|
|
468
|
+
|
|
469
|
+
if summary:
|
|
470
|
+
for deviation in deviation_list:
|
|
471
|
+
_ = deviation.pop('check_type')
|
|
472
|
+
_ = deviation.pop('percentage')
|
|
473
|
+
_ = deviation.pop('ma_value_checked')
|
|
474
|
+
_ = deviation.pop('deviation_type')
|
|
475
|
+
_ = deviation.pop('data')
|
|
476
|
+
_ = deviation.pop('ma_data')
|
|
477
|
+
|
|
478
|
+
if output_file_path:
|
|
479
|
+
print_api(f'Deviation Found, saving to file: {output_file_path}', color='blue')
|
|
480
|
+
|
|
481
|
+
if output_file_type == 'csv':
|
|
482
|
+
csvs.write_list_to_csv(deviation_list, output_file_path)
|
|
483
|
+
elif output_file_type == 'json':
|
|
484
|
+
jsons.write_json_file(deviation_list, output_file_path, use_default_indent=True)
|
|
485
|
+
|
|
486
|
+
return deviation_list
|
|
487
|
+
|
|
488
|
+
return []
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def deviation_calculator_by_moving_average_main():
|
|
492
|
+
def parse_args():
|
|
493
|
+
description_full: str = (
|
|
494
|
+
f'Description: Calculate deviation by moving average.')
|
|
495
|
+
|
|
496
|
+
parser = argparse.ArgumentParser(
|
|
497
|
+
description=description_full)
|
|
498
|
+
|
|
499
|
+
parser.add_argument(
|
|
500
|
+
'-d', '--directory', type=str, required=True,
|
|
501
|
+
help='Provide full path to directory with statistics.csv files.')
|
|
502
|
+
parser.add_argument(
|
|
503
|
+
'-of', '--output_file', type=str, required=True, help='Provide full path to output file.')
|
|
504
|
+
parser.add_argument(
|
|
505
|
+
'-ot', '--output_type', type=str, required=True, help='Provide output type: [json] or [csv].')
|
|
506
|
+
parser.add_argument(
|
|
507
|
+
'-by', '--by_type', type=str, required=True, help='Calculate by [host] or [url].')
|
|
508
|
+
parser.add_argument(
|
|
509
|
+
'-f', '--full_details', action='store_true', required=False,
|
|
510
|
+
help='(OPTIONAL) Output full processing details instead of summary.')
|
|
511
|
+
parser.add_argument(
|
|
512
|
+
'-w', '--window', type=int, required=True, help='Moving average window in days.')
|
|
513
|
+
parser.add_argument(
|
|
514
|
+
'-p', '--percentage', type=float, required=True,
|
|
515
|
+
help='Percentage of deviation from moving average. Example: 0.1 for 10%%.')
|
|
516
|
+
parser.add_argument(
|
|
517
|
+
'-slt', '--skip_total_count_less_than', type=int, required=False,
|
|
518
|
+
help='An integer to skip the deviation calculation if the total count is less than this number.')
|
|
519
|
+
|
|
520
|
+
return parser.parse_args()
|
|
521
|
+
|
|
522
|
+
args = parse_args()
|
|
523
|
+
|
|
524
|
+
if args.output_type == 'csv' and args.full_details:
|
|
525
|
+
print_api("Full details output is not supported for csv files.", color='red')
|
|
526
|
+
return 1
|
|
527
|
+
|
|
528
|
+
if not os.path.isdir(args.directory):
|
|
529
|
+
print_api(f"Directory does not exist: {args.directory}", color='red')
|
|
530
|
+
return 1
|
|
531
|
+
|
|
532
|
+
if not filesystem.get_paths_from_directory(
|
|
533
|
+
directory_path=args.directory, get_file=True, simple_list=True, file_name_check_pattern='*statistics*.csv'):
|
|
534
|
+
print_api(f"No statistics files found in: {args.directory}", color='red')
|
|
535
|
+
return 1
|
|
536
|
+
|
|
537
|
+
if args.full_details:
|
|
538
|
+
summary = False
|
|
539
|
+
convert_sizes_lists_and_ma_data_to_string = True
|
|
540
|
+
else:
|
|
541
|
+
summary = True
|
|
542
|
+
convert_sizes_lists_and_ma_data_to_string = False
|
|
543
|
+
|
|
544
|
+
_ = deviation_calculator_by_moving_average(
|
|
545
|
+
statistics_file_directory=args.directory,
|
|
546
|
+
by_type=args.by_type,
|
|
547
|
+
moving_average_window_days=args.window,
|
|
548
|
+
top_bottom_deviation_percentage=args.percentage,
|
|
549
|
+
summary=summary,
|
|
550
|
+
output_file_path=args.output_file,
|
|
551
|
+
output_file_type=args.output_type,
|
|
552
|
+
convert_sizes_lists_and_ma_data_to_string=convert_sizes_lists_and_ma_data_to_string,
|
|
553
|
+
skip_total_count_less_than=args.skip_total_count_less_than
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
return 0
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
from ... import datetimes
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_the_last_day_number(statistics_content: list, stop_after_lines: int = None) -> int:
|
|
7
|
+
"""
|
|
8
|
+
This function gets the last day number from the statistics content.
|
|
9
|
+
|
|
10
|
+
:param statistics_content: list, of lines in the statistics content.
|
|
11
|
+
:param stop_after_lines: integer, if specified, the function will stop after the specified number of lines.
|
|
12
|
+
:return: integer, the last day number.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
last_day_number = None
|
|
16
|
+
start_time_temp = None
|
|
17
|
+
for line_index, line in enumerate(statistics_content):
|
|
18
|
+
try:
|
|
19
|
+
request_time = datetime.datetime.strptime(line['request_time_sent'], '%Y-%m-%d %H:%M:%S.%f')
|
|
20
|
+
except ValueError:
|
|
21
|
+
continue
|
|
22
|
+
|
|
23
|
+
if not start_time_temp:
|
|
24
|
+
start_time_temp = request_time
|
|
25
|
+
|
|
26
|
+
if stop_after_lines:
|
|
27
|
+
if line_index == stop_after_lines:
|
|
28
|
+
break
|
|
29
|
+
|
|
30
|
+
last_day_number = datetimes.get_difference_between_dates_in_days(start_time_temp, request_time)
|
|
31
|
+
return last_day_number
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_empty_features_dict() -> dict:
|
|
35
|
+
"""
|
|
36
|
+
This function creates an empty dictionary for the daily stats. This should be initiated for each 'host_type' of:
|
|
37
|
+
'domain', 'subdomain', 'url_no_parameters'.
|
|
38
|
+
:return: dict
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
'total_count': {}, 'normal_count': {}, 'error_count': {},
|
|
43
|
+
'request_0_byte_count': {}, 'response_0_byte_count': {},
|
|
44
|
+
'request_sizes_list': {}, 'response_sizes_list': {},
|
|
45
|
+
'request_sizes_no_0_bytes_list': {}, 'response_sizes_no_0_bytes_list': {},
|
|
46
|
+
'average_request_size': {}, 'average_response_size': {},
|
|
47
|
+
'average_request_size_no_0_bytes': {}, 'average_response_size_no_0_bytes': {}}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def add_to_count_to_daily_stats(
|
|
51
|
+
daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str) -> None:
|
|
52
|
+
"""
|
|
53
|
+
This function adds 1 to the 'count' feature of the current day in the daily stats.
|
|
54
|
+
|
|
55
|
+
:param daily_stats: dict, the daily statistics dict.
|
|
56
|
+
:param current_day: integer, the current day number.
|
|
57
|
+
:param last_day: integer, the last day number.
|
|
58
|
+
:param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
|
|
59
|
+
:param feature: string, the feature to add the count to. Can be: 'total_count', 'normal_count', 'error_count',
|
|
60
|
+
'request_0_byte_count', 'response_0_byte_count'.
|
|
61
|
+
:param host_name: string, the name of the host.
|
|
62
|
+
|
|
63
|
+
:return: None.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
# Aggregate daily domain hits.
|
|
67
|
+
if host_name not in daily_stats[host_type][feature].keys():
|
|
68
|
+
daily_stats[host_type][feature][host_name] = {}
|
|
69
|
+
# Iterate from first day to the last day.
|
|
70
|
+
for day in range(0, last_day + 1):
|
|
71
|
+
daily_stats[host_type][feature][host_name][day] = 0
|
|
72
|
+
|
|
73
|
+
# Add count to current day.
|
|
74
|
+
daily_stats[host_type][feature][host_name][current_day] += 1
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def add_to_list_to_daily_stats(
|
|
78
|
+
daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str,
|
|
79
|
+
size: float) -> None:
|
|
80
|
+
"""
|
|
81
|
+
This function adds the 'size' to the 'feature' list of the current day in the daily stats.
|
|
82
|
+
|
|
83
|
+
:param daily_stats: dict, the daily statistics dict.
|
|
84
|
+
:param current_day: integer, the current day number.
|
|
85
|
+
:param last_day: integer, the last day number.
|
|
86
|
+
:param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
|
|
87
|
+
:param feature: string, the feature to add the count to. Can be: 'request_sizes_list', 'response_sizes_list',
|
|
88
|
+
'request_sizes_no_0_bytes_list', 'response_sizes_no_0_bytes_list'.
|
|
89
|
+
:param host_name: string, the name of the host.
|
|
90
|
+
:param size: float, the size in bytes to add to the list.
|
|
91
|
+
|
|
92
|
+
:return: None.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
# Aggregate daily domain hits.
|
|
96
|
+
if host_name not in daily_stats[host_type][feature].keys():
|
|
97
|
+
daily_stats[host_type][feature][host_name] = {}
|
|
98
|
+
# Iterate from first day to the last day.
|
|
99
|
+
for day in range(0, last_day + 1):
|
|
100
|
+
daily_stats[host_type][feature][host_name][day] = []
|
|
101
|
+
|
|
102
|
+
# Add count to current day.
|
|
103
|
+
daily_stats[host_type][feature][host_name][current_day].append(size)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def add_to_average_to_daily_stats(
|
|
107
|
+
daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str,
|
|
108
|
+
list_of_sizes: list) -> None:
|
|
109
|
+
"""
|
|
110
|
+
This function adds the average size in bytes calculated from the 'list_of_sizes' to the 'feature' of the current
|
|
111
|
+
day in the daily stats.
|
|
112
|
+
|
|
113
|
+
:param daily_stats: dict, the daily statistics dict.
|
|
114
|
+
:param current_day: integer, the current day number.
|
|
115
|
+
:param last_day: integer, the last day number.
|
|
116
|
+
:param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
|
|
117
|
+
:param feature: string, the feature to add the count to. Can be: 'average_request_size', 'average_response_size',
|
|
118
|
+
'average_request_size_no_0_bytes', 'average_response_size_no_0_bytes'.
|
|
119
|
+
:param host_name: string, the name of the host.
|
|
120
|
+
:param list_of_sizes: list, the list of sizes to calculate the average from.
|
|
121
|
+
|
|
122
|
+
:return: None.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
# Aggregate daily domain hits.
|
|
126
|
+
if host_name not in daily_stats[host_type][feature].keys():
|
|
127
|
+
daily_stats[host_type][feature][host_name] = {}
|
|
128
|
+
# Iterate from first day to the last day.
|
|
129
|
+
for day in range(0, last_day + 1):
|
|
130
|
+
daily_stats[host_type][feature][host_name][day] = 0
|
|
131
|
+
|
|
132
|
+
# If the list of size is empty, add 0 to the average, since we cannot divide by 0.
|
|
133
|
+
if len(list_of_sizes) == 0:
|
|
134
|
+
daily_stats[host_type][feature][host_name][current_day] = 0
|
|
135
|
+
else:
|
|
136
|
+
daily_stats[host_type][feature][host_name][current_day] = sum(list_of_sizes) / len(list_of_sizes)
|