souleyez 2.43.28__py3-none-any.whl → 2.43.32__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.
Files changed (356) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9592 -2879
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1238 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2198 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +563 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +408 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +371 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +288 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/whois_handler.py +277 -0
  126. souleyez/handlers/wpscan_handler.py +554 -0
  127. souleyez/history.py +32 -16
  128. souleyez/importers/msf_importer.py +106 -75
  129. souleyez/importers/smart_importer.py +208 -147
  130. souleyez/integrations/siem/__init__.py +10 -10
  131. souleyez/integrations/siem/base.py +17 -18
  132. souleyez/integrations/siem/elastic.py +108 -122
  133. souleyez/integrations/siem/factory.py +207 -80
  134. souleyez/integrations/siem/googlesecops.py +146 -154
  135. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  136. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  137. souleyez/integrations/siem/sentinel.py +107 -109
  138. souleyez/integrations/siem/splunk.py +246 -212
  139. souleyez/integrations/siem/wazuh.py +65 -71
  140. souleyez/integrations/wazuh/__init__.py +5 -5
  141. souleyez/integrations/wazuh/client.py +70 -93
  142. souleyez/integrations/wazuh/config.py +85 -57
  143. souleyez/integrations/wazuh/host_mapper.py +28 -36
  144. souleyez/integrations/wazuh/sync.py +78 -68
  145. souleyez/intelligence/__init__.py +4 -5
  146. souleyez/intelligence/correlation_analyzer.py +309 -295
  147. souleyez/intelligence/exploit_knowledge.py +661 -623
  148. souleyez/intelligence/exploit_suggestions.py +159 -139
  149. souleyez/intelligence/gap_analyzer.py +132 -97
  150. souleyez/intelligence/gap_detector.py +251 -214
  151. souleyez/intelligence/sensitive_tables.py +266 -129
  152. souleyez/intelligence/service_parser.py +137 -123
  153. souleyez/intelligence/surface_analyzer.py +407 -268
  154. souleyez/intelligence/target_parser.py +159 -162
  155. souleyez/licensing/__init__.py +6 -6
  156. souleyez/licensing/validator.py +17 -19
  157. souleyez/log_config.py +79 -54
  158. souleyez/main.py +1505 -687
  159. souleyez/migrations/fix_job_counter.py +16 -14
  160. souleyez/parsers/bloodhound_parser.py +41 -39
  161. souleyez/parsers/crackmapexec_parser.py +178 -111
  162. souleyez/parsers/dalfox_parser.py +72 -77
  163. souleyez/parsers/dnsrecon_parser.py +103 -91
  164. souleyez/parsers/enum4linux_parser.py +183 -153
  165. souleyez/parsers/ffuf_parser.py +29 -25
  166. souleyez/parsers/gobuster_parser.py +301 -41
  167. souleyez/parsers/hashcat_parser.py +324 -79
  168. souleyez/parsers/http_fingerprint_parser.py +350 -103
  169. souleyez/parsers/hydra_parser.py +131 -111
  170. souleyez/parsers/impacket_parser.py +231 -178
  171. souleyez/parsers/john_parser.py +98 -86
  172. souleyez/parsers/katana_parser.py +316 -0
  173. souleyez/parsers/msf_parser.py +943 -498
  174. souleyez/parsers/nikto_parser.py +346 -65
  175. souleyez/parsers/nmap_parser.py +262 -174
  176. souleyez/parsers/nuclei_parser.py +40 -44
  177. souleyez/parsers/responder_parser.py +26 -26
  178. souleyez/parsers/searchsploit_parser.py +74 -74
  179. souleyez/parsers/service_explorer_parser.py +279 -0
  180. souleyez/parsers/smbmap_parser.py +180 -124
  181. souleyez/parsers/sqlmap_parser.py +434 -308
  182. souleyez/parsers/theharvester_parser.py +75 -57
  183. souleyez/parsers/whois_parser.py +135 -94
  184. souleyez/parsers/wpscan_parser.py +278 -190
  185. souleyez/plugins/afp.py +44 -36
  186. souleyez/plugins/afp_brute.py +114 -46
  187. souleyez/plugins/ard.py +48 -37
  188. souleyez/plugins/bloodhound.py +95 -61
  189. souleyez/plugins/certipy.py +303 -0
  190. souleyez/plugins/crackmapexec.py +186 -85
  191. souleyez/plugins/dalfox.py +120 -59
  192. souleyez/plugins/dns_hijack.py +146 -41
  193. souleyez/plugins/dnsrecon.py +97 -61
  194. souleyez/plugins/enum4linux.py +91 -66
  195. souleyez/plugins/evil_winrm.py +291 -0
  196. souleyez/plugins/ffuf.py +166 -90
  197. souleyez/plugins/firmware_extract.py +133 -29
  198. souleyez/plugins/gobuster.py +387 -190
  199. souleyez/plugins/gpp_extract.py +393 -0
  200. souleyez/plugins/hashcat.py +100 -73
  201. souleyez/plugins/http_fingerprint.py +854 -267
  202. souleyez/plugins/hydra.py +566 -200
  203. souleyez/plugins/impacket_getnpusers.py +117 -69
  204. souleyez/plugins/impacket_psexec.py +84 -64
  205. souleyez/plugins/impacket_secretsdump.py +103 -69
  206. souleyez/plugins/impacket_smbclient.py +89 -75
  207. souleyez/plugins/john.py +86 -69
  208. souleyez/plugins/katana.py +313 -0
  209. souleyez/plugins/kerbrute.py +237 -0
  210. souleyez/plugins/lfi_extract.py +541 -0
  211. souleyez/plugins/macos_ssh.py +117 -48
  212. souleyez/plugins/mdns.py +35 -30
  213. souleyez/plugins/msf_auxiliary.py +253 -130
  214. souleyez/plugins/msf_exploit.py +239 -161
  215. souleyez/plugins/nikto.py +134 -78
  216. souleyez/plugins/nmap.py +275 -91
  217. souleyez/plugins/nuclei.py +180 -89
  218. souleyez/plugins/nxc.py +285 -0
  219. souleyez/plugins/plugin_base.py +35 -36
  220. souleyez/plugins/plugin_template.py +13 -5
  221. souleyez/plugins/rdp_sec_check.py +130 -0
  222. souleyez/plugins/responder.py +112 -71
  223. souleyez/plugins/router_http_brute.py +76 -65
  224. souleyez/plugins/router_ssh_brute.py +118 -41
  225. souleyez/plugins/router_telnet_brute.py +124 -42
  226. souleyez/plugins/routersploit.py +91 -59
  227. souleyez/plugins/routersploit_exploit.py +77 -55
  228. souleyez/plugins/searchsploit.py +91 -77
  229. souleyez/plugins/service_explorer.py +1160 -0
  230. souleyez/plugins/smbmap.py +122 -72
  231. souleyez/plugins/smbpasswd.py +215 -0
  232. souleyez/plugins/sqlmap.py +301 -113
  233. souleyez/plugins/theharvester.py +127 -75
  234. souleyez/plugins/tr069.py +79 -57
  235. souleyez/plugins/upnp.py +65 -47
  236. souleyez/plugins/upnp_abuse.py +73 -55
  237. souleyez/plugins/vnc_access.py +129 -42
  238. souleyez/plugins/vnc_brute.py +109 -38
  239. souleyez/plugins/whois.py +77 -58
  240. souleyez/plugins/wpscan.py +173 -69
  241. souleyez/reporting/__init__.py +2 -1
  242. souleyez/reporting/attack_chain.py +411 -346
  243. souleyez/reporting/charts.py +436 -501
  244. souleyez/reporting/compliance_mappings.py +334 -201
  245. souleyez/reporting/detection_report.py +126 -125
  246. souleyez/reporting/formatters.py +828 -591
  247. souleyez/reporting/generator.py +386 -302
  248. souleyez/reporting/metrics.py +72 -75
  249. souleyez/scanner.py +35 -29
  250. souleyez/security/__init__.py +37 -11
  251. souleyez/security/scope_validator.py +175 -106
  252. souleyez/security/validation.py +223 -149
  253. souleyez/security.py +22 -6
  254. souleyez/storage/credentials.py +247 -186
  255. souleyez/storage/crypto.py +296 -129
  256. souleyez/storage/database.py +73 -50
  257. souleyez/storage/db.py +58 -36
  258. souleyez/storage/deliverable_evidence.py +177 -128
  259. souleyez/storage/deliverable_exporter.py +282 -246
  260. souleyez/storage/deliverable_templates.py +134 -116
  261. souleyez/storage/deliverables.py +135 -130
  262. souleyez/storage/engagements.py +109 -56
  263. souleyez/storage/evidence.py +181 -152
  264. souleyez/storage/execution_log.py +31 -17
  265. souleyez/storage/exploit_attempts.py +93 -57
  266. souleyez/storage/exploits.py +67 -36
  267. souleyez/storage/findings.py +48 -61
  268. souleyez/storage/hosts.py +176 -144
  269. souleyez/storage/migrate_to_engagements.py +43 -19
  270. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  271. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  272. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  273. souleyez/storage/migrations/_005_screenshots.py +13 -5
  274. souleyez/storage/migrations/_006_deliverables.py +13 -5
  275. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  276. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  277. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  278. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  279. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  280. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  281. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  282. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  283. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  284. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  285. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  286. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  287. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  288. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  289. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  290. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  291. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  292. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  293. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  294. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  295. souleyez/storage/migrations/__init__.py +26 -26
  296. souleyez/storage/migrations/migration_manager.py +19 -19
  297. souleyez/storage/msf_sessions.py +100 -65
  298. souleyez/storage/osint.py +17 -24
  299. souleyez/storage/recommendation_engine.py +269 -235
  300. souleyez/storage/screenshots.py +33 -32
  301. souleyez/storage/smb_shares.py +136 -92
  302. souleyez/storage/sqlmap_data.py +183 -128
  303. souleyez/storage/team_collaboration.py +135 -141
  304. souleyez/storage/timeline_tracker.py +122 -94
  305. souleyez/storage/wazuh_vulns.py +64 -66
  306. souleyez/storage/web_paths.py +33 -37
  307. souleyez/testing/credential_tester.py +221 -205
  308. souleyez/ui/__init__.py +1 -1
  309. souleyez/ui/ai_quotes.py +12 -12
  310. souleyez/ui/attack_surface.py +2439 -1516
  311. souleyez/ui/chain_rules_view.py +914 -382
  312. souleyez/ui/correlation_view.py +312 -230
  313. souleyez/ui/dashboard.py +2382 -1130
  314. souleyez/ui/deliverables_view.py +148 -62
  315. souleyez/ui/design_system.py +13 -13
  316. souleyez/ui/errors.py +49 -49
  317. souleyez/ui/evidence_linking_view.py +284 -179
  318. souleyez/ui/evidence_vault.py +393 -285
  319. souleyez/ui/exploit_suggestions_view.py +555 -349
  320. souleyez/ui/export_view.py +100 -66
  321. souleyez/ui/gap_analysis_view.py +315 -171
  322. souleyez/ui/help_system.py +105 -97
  323. souleyez/ui/intelligence_view.py +436 -293
  324. souleyez/ui/interactive.py +23142 -10430
  325. souleyez/ui/interactive_selector.py +75 -68
  326. souleyez/ui/log_formatter.py +47 -39
  327. souleyez/ui/menu_components.py +22 -13
  328. souleyez/ui/msf_auxiliary_menu.py +184 -133
  329. souleyez/ui/pending_chains_view.py +336 -172
  330. souleyez/ui/progress_indicators.py +5 -3
  331. souleyez/ui/recommendations_view.py +195 -137
  332. souleyez/ui/rule_builder.py +343 -225
  333. souleyez/ui/setup_wizard.py +678 -284
  334. souleyez/ui/shortcuts.py +217 -165
  335. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  336. souleyez/ui/splunk_vulns_view.py +139 -86
  337. souleyez/ui/team_dashboard.py +498 -335
  338. souleyez/ui/template_selector.py +196 -105
  339. souleyez/ui/terminal.py +6 -6
  340. souleyez/ui/timeline_view.py +198 -127
  341. souleyez/ui/tool_setup.py +264 -164
  342. souleyez/ui/tutorial.py +202 -72
  343. souleyez/ui/tutorial_state.py +40 -40
  344. souleyez/ui/wazuh_vulns_view.py +235 -141
  345. souleyez/ui/wordlist_browser.py +260 -107
  346. souleyez/ui.py +464 -312
  347. souleyez/utils/tool_checker.py +427 -367
  348. souleyez/utils.py +33 -29
  349. souleyez/wordlists.py +134 -167
  350. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/METADATA +1 -1
  351. souleyez-2.43.32.dist-info/RECORD +441 -0
  352. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/WHEEL +1 -1
  353. souleyez-2.43.28.dist-info/RECORD +0 -379
  354. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/entry_points.txt +0 -0
  355. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/licenses/LICENSE +0 -0
  356. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/top_level.txt +0 -0
@@ -9,108 +9,103 @@ from datetime import datetime
9
9
 
10
10
  class MetricsCalculator:
11
11
  """Calculate executive dashboard metrics and risk scores."""
12
-
12
+
13
13
  def __init__(self):
14
14
  pass
15
-
15
+
16
16
  def calculate_risk_score(self, findings_by_severity: Dict) -> int:
17
17
  """
18
18
  Calculate overall risk score (0-100).
19
-
19
+
20
20
  Weighted scoring:
21
21
  - Critical: 25 points each
22
22
  - High: 10 points each
23
23
  - Medium: 3 points each
24
24
  - Low: 1 point each
25
25
  - Info: 0 points
26
-
26
+
27
27
  Capped at 100.
28
28
  """
29
29
  score = 0
30
- score += len(findings_by_severity.get('critical', [])) * 25
31
- score += len(findings_by_severity.get('high', [])) * 10
32
- score += len(findings_by_severity.get('medium', [])) * 3
33
- score += len(findings_by_severity.get('low', [])) * 1
34
-
30
+ score += len(findings_by_severity.get("critical", [])) * 25
31
+ score += len(findings_by_severity.get("high", [])) * 10
32
+ score += len(findings_by_severity.get("medium", [])) * 3
33
+ score += len(findings_by_severity.get("low", [])) * 1
34
+
35
35
  return min(score, 100)
36
-
36
+
37
37
  def calculate_exploitation_rate(self, attack_surface: Dict) -> float:
38
38
  """Calculate percentage of services successfully exploited."""
39
- overview = attack_surface.get('overview', {})
40
- total_services = overview.get('total_services', 0)
41
- exploited = overview.get('exploited_services', 0)
42
-
39
+ overview = attack_surface.get("overview", {})
40
+ total_services = overview.get("total_services", 0)
41
+ exploited = overview.get("exploited_services", 0)
42
+
43
43
  if total_services == 0:
44
44
  return 0.0
45
-
45
+
46
46
  return round((exploited / total_services) * 100, 1)
47
-
47
+
48
48
  def estimate_remediation_timeline(self, findings_by_severity: Dict) -> Dict:
49
49
  """
50
50
  Estimate remediation timeline in days.
51
-
51
+
52
52
  Estimates:
53
53
  - Critical: 1 day each
54
54
  - High: 0.5 days each
55
55
  - Medium: 0.25 days each
56
56
  - Low: 0.1 days each
57
57
  """
58
- critical_days = len(findings_by_severity.get('critical', [])) * 1
59
- high_days = len(findings_by_severity.get('high', [])) * 0.5
60
- medium_days = len(findings_by_severity.get('medium', [])) * 0.25
61
- low_days = len(findings_by_severity.get('low', [])) * 0.1
62
-
58
+ critical_days = len(findings_by_severity.get("critical", [])) * 1
59
+ high_days = len(findings_by_severity.get("high", [])) * 0.5
60
+ medium_days = len(findings_by_severity.get("medium", [])) * 0.25
61
+ low_days = len(findings_by_severity.get("low", [])) * 0.1
62
+
63
63
  total_days = critical_days + high_days + medium_days + low_days
64
-
64
+
65
65
  return {
66
- 'total_days': round(total_days, 1),
67
- 'weeks': round(total_days / 5, 1), # Business days
68
- 'critical': round(critical_days, 1),
69
- 'high': round(high_days, 1),
70
- 'medium': round(medium_days, 1),
71
- 'low': round(low_days, 1)
66
+ "total_days": round(total_days, 1),
67
+ "weeks": round(total_days / 5, 1), # Business days
68
+ "critical": round(critical_days, 1),
69
+ "high": round(high_days, 1),
70
+ "medium": round(medium_days, 1),
71
+ "low": round(low_days, 1),
72
72
  }
73
-
73
+
74
74
  def calculate_host_risk_distribution(self, attack_surface: Dict) -> Dict:
75
75
  """Calculate risk distribution across hosts."""
76
- hosts = attack_surface.get('hosts', [])
77
-
78
- distribution = {
79
- 'critical': 0,
80
- 'high': 0,
81
- 'medium': 0,
82
- 'low': 0
83
- }
84
-
76
+ hosts = attack_surface.get("hosts", [])
77
+
78
+ distribution = {"critical": 0, "high": 0, "medium": 0, "low": 0}
79
+
85
80
  for host in hosts:
86
- critical_findings = host.get('critical_findings', 0)
87
- total_findings = host.get('findings', 0)
88
-
81
+ critical_findings = host.get("critical_findings", 0)
82
+ total_findings = host.get("findings", 0)
83
+
89
84
  if critical_findings > 0:
90
- distribution['critical'] += 1
85
+ distribution["critical"] += 1
91
86
  elif total_findings >= 5:
92
- distribution['high'] += 1
87
+ distribution["high"] += 1
93
88
  elif total_findings >= 2:
94
- distribution['medium'] += 1
89
+ distribution["medium"] += 1
95
90
  else:
96
- distribution['low'] += 1
97
-
91
+ distribution["low"] += 1
92
+
98
93
  return distribution
99
-
94
+
100
95
  def get_dashboard_metrics(self, data: Dict) -> Dict:
101
96
  """
102
97
  Generate all executive dashboard metrics.
103
-
98
+
104
99
  Returns comprehensive metrics dict for dashboard rendering.
105
100
  """
106
- findings_by_severity = data['findings_by_severity']
107
- attack_surface = data['attack_surface']
108
-
101
+ findings_by_severity = data["findings_by_severity"]
102
+ attack_surface = data["attack_surface"]
103
+
109
104
  risk_score = self.calculate_risk_score(findings_by_severity)
110
105
  exploitation_rate = self.calculate_exploitation_rate(attack_surface)
111
106
  remediation = self.estimate_remediation_timeline(findings_by_severity)
112
107
  host_distribution = self.calculate_host_risk_distribution(attack_surface)
113
-
108
+
114
109
  # Determine risk level
115
110
  if risk_score >= 75:
116
111
  risk_level = "CRITICAL"
@@ -124,29 +119,31 @@ class MetricsCalculator:
124
119
  else:
125
120
  risk_level = "LOW"
126
121
  risk_color = "green"
127
-
122
+
128
123
  # Total findings
129
- total_findings = sum(len(findings) for findings in findings_by_severity.values())
130
-
124
+ total_findings = sum(
125
+ len(findings) for findings in findings_by_severity.values()
126
+ )
127
+
131
128
  # Overview stats
132
- overview = attack_surface.get('overview', {})
133
-
129
+ overview = attack_surface.get("overview", {})
130
+
134
131
  return {
135
- 'risk_score': risk_score,
136
- 'risk_level': risk_level,
137
- 'risk_color': risk_color,
138
- 'total_findings': total_findings,
139
- 'critical_findings': len(findings_by_severity.get('critical', [])),
140
- 'high_findings': len(findings_by_severity.get('high', [])),
141
- 'medium_findings': len(findings_by_severity.get('medium', [])),
142
- 'low_findings': len(findings_by_severity.get('low', [])),
143
- 'info_findings': len(findings_by_severity.get('info', [])),
144
- 'total_hosts': overview.get('total_hosts', 0),
145
- 'vulnerable_hosts': overview.get('vulnerable_hosts', 0),
146
- 'total_services': overview.get('total_services', 0),
147
- 'exploited_services': overview.get('exploited_services', 0),
148
- 'exploitation_rate': exploitation_rate,
149
- 'remediation_timeline': remediation,
150
- 'host_risk_distribution': host_distribution,
151
- 'credentials_found': len(data.get('credentials', []))
132
+ "risk_score": risk_score,
133
+ "risk_level": risk_level,
134
+ "risk_color": risk_color,
135
+ "total_findings": total_findings,
136
+ "critical_findings": len(findings_by_severity.get("critical", [])),
137
+ "high_findings": len(findings_by_severity.get("high", [])),
138
+ "medium_findings": len(findings_by_severity.get("medium", [])),
139
+ "low_findings": len(findings_by_severity.get("low", [])),
140
+ "info_findings": len(findings_by_severity.get("info", [])),
141
+ "total_hosts": overview.get("total_hosts", 0),
142
+ "vulnerable_hosts": overview.get("vulnerable_hosts", 0),
143
+ "total_services": overview.get("total_services", 0),
144
+ "exploited_services": overview.get("exploited_services", 0),
145
+ "exploitation_rate": exploitation_rate,
146
+ "remediation_timeline": remediation,
147
+ "host_risk_distribution": host_distribution,
148
+ "credentials_found": len(data.get("credentials", [])),
152
149
  }
souleyez/scanner.py CHANGED
@@ -11,10 +11,10 @@ logger = get_logger(__name__)
11
11
 
12
12
 
13
13
  def _make_paths(target, label):
14
- safe_label = (label.replace(' ', '_') if label else 'scan')
14
+ safe_label = label.replace(" ", "_") if label else "scan"
15
15
  ts = timestamp_str()
16
16
  base = f'nmap_{ts}_{safe_label}_{target.replace("/", "_")}'
17
- return SCANS_DIR / (base + '.log'), SCANS_DIR / (base + '.xml')
17
+ return SCANS_DIR / (base + ".log"), SCANS_DIR / (base + ".xml")
18
18
 
19
19
 
20
20
  def _parse_nmap_xml_summary(xmlpath: Path):
@@ -23,54 +23,60 @@ def _parse_nmap_xml_summary(xmlpath: Path):
23
23
  root = tree.getroot()
24
24
  except Exception:
25
25
  return None
26
- hosts = root.findall('host')
26
+ hosts = root.findall("host")
27
27
  hosts_total = len(hosts)
28
28
  hosts_up = 0
29
29
  open_ports = 0
30
30
  per_host = []
31
31
  for h in hosts:
32
32
  addr = None
33
- a = h.find('address')
33
+ a = h.find("address")
34
34
  if a is not None:
35
- addr = a.get('addr')
36
- st = h.find('status')
37
- up = (st is not None and st.get('state') == 'up')
35
+ addr = a.get("addr")
36
+ st = h.find("status")
37
+ up = st is not None and st.get("state") == "up"
38
38
  if up:
39
39
  hosts_up += 1
40
- ports = h.find('ports')
40
+ ports = h.find("ports")
41
41
  open_count = 0
42
42
  if ports is not None:
43
- for p in ports.findall('port'):
44
- state = p.find('state')
45
- if state is not None and state.get('state') == 'open':
43
+ for p in ports.findall("port"):
44
+ state = p.find("state")
45
+ if state is not None and state.get("state") == "open":
46
46
  open_count += 1
47
47
  open_ports += open_count
48
- per_host.append({'addr': addr, 'up': up, 'open': open_count})
49
- return {'hosts_total': hosts_total, 'hosts_up': hosts_up, 'open_ports': open_ports, 'per_host': per_host}
48
+ per_host.append({"addr": addr, "up": up, "open": open_count})
49
+ return {
50
+ "hosts_total": hosts_total,
51
+ "hosts_up": hosts_up,
52
+ "open_ports": open_ports,
53
+ "per_host": per_host,
54
+ }
50
55
 
51
56
 
52
57
  def run_nmap(target, nmap_args, label=None, save_xml=False):
53
58
  if not nmap_installed():
54
- raise EnvironmentError('nmap is not installed or not on PATH.')
55
-
59
+ raise EnvironmentError("nmap is not installed or not on PATH.")
60
+
56
61
  # Validate nmap args to prevent command injection
57
62
  try:
58
63
  validated_args = validate_nmap_args(nmap_args)
59
64
  except ValidationError as e:
60
- logger.error("Invalid nmap arguments", extra={
61
- "args": nmap_args,
62
- "error": str(e)
63
- })
65
+ logger.error(
66
+ "Invalid nmap arguments", extra={"args": nmap_args, "error": str(e)}
67
+ )
64
68
  raise
65
-
66
- logpath, xmlpath = _make_paths(target, label or 'scan')
67
- cmd = ['nmap'] + list(validated_args)
69
+
70
+ logpath, xmlpath = _make_paths(target, label or "scan")
71
+ cmd = ["nmap"] + list(validated_args)
68
72
  if save_xml:
69
- cmd += ['-oX', str(xmlpath)]
73
+ cmd += ["-oX", str(xmlpath)]
70
74
  cmd += [target]
71
- with open(logpath, 'w', encoding='utf-8', errors='replace') as lf:
72
- lf.write(f'$ {join_cmd(cmd)}\\n\\n')
73
- proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True)
75
+ with open(logpath, "w", encoding="utf-8", errors="replace") as lf:
76
+ lf.write(f"$ {join_cmd(cmd)}\\n\\n")
77
+ proc = subprocess.Popen(
78
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True
79
+ )
74
80
  try:
75
81
  for line in proc.stdout:
76
82
  print(line.rstrip())
@@ -78,11 +84,11 @@ def run_nmap(target, nmap_args, label=None, save_xml=False):
78
84
  except KeyboardInterrupt:
79
85
  proc.terminate()
80
86
  proc.wait(timeout=5)
81
- print('\\n[scan cancelled by user]')
82
- lf.write('\\n[scan cancelled by user]\\n')
87
+ print("\\n[scan cancelled by user]")
88
+ lf.write("\\n[scan cancelled by user]\\n")
83
89
  return logpath, -1, (xmlpath if save_xml else None), None
84
90
  rc = proc.wait()
85
- lf.write(f'\\n# exit_code: {rc}\\n')
91
+ lf.write(f"\\n# exit_code: {rc}\\n")
86
92
  summary = None
87
93
  if save_xml and xmlpath.exists():
88
94
  summary = _parse_nmap_xml_summary(xmlpath)
@@ -1,4 +1,5 @@
1
1
  """Security validation, sanitization, and authentication utilities."""
2
+
2
3
  import click
3
4
  import sys
4
5
  import functools
@@ -16,6 +17,7 @@ def require_login(f):
16
17
  def my_command():
17
18
  ...
18
19
  """
20
+
19
21
  @functools.wraps(f)
20
22
  def wrapper(*args, **kwargs):
21
23
  from souleyez.auth import get_current_user, is_logged_in, init_auth
@@ -24,16 +26,18 @@ def require_login(f):
24
26
  # Initialize auth if needed
25
27
  try:
26
28
  from souleyez.auth import get_session_manager
29
+
27
30
  get_session_manager()
28
31
  except RuntimeError:
29
32
  init_auth(get_db().db_path)
30
33
 
31
34
  if not is_logged_in():
32
- click.echo(click.style("\n⚠️ Authentication required.", fg='yellow'))
33
- click.echo(click.style(" Run: souleyez login", fg='yellow'))
35
+ click.echo(click.style("\n⚠️ Authentication required.", fg="yellow"))
36
+ click.echo(click.style(" Run: souleyez login", fg="yellow"))
34
37
  sys.exit(1)
35
38
 
36
39
  return f(*args, **kwargs)
40
+
37
41
  return wrapper
38
42
 
39
43
 
@@ -49,16 +53,18 @@ def require_admin(f):
49
53
  def admin_only_command():
50
54
  ...
51
55
  """
56
+
52
57
  @functools.wraps(f)
53
58
  def wrapper(*args, **kwargs):
54
59
  from souleyez.auth import get_current_user, Role
55
60
 
56
61
  user = get_current_user()
57
62
  if user is None or user.role != Role.ADMIN:
58
- click.echo(click.style("\n⚠️ Admin privileges required.", fg='red'))
63
+ click.echo(click.style("\n⚠️ Admin privileges required.", fg="red"))
59
64
  sys.exit(1)
60
65
 
61
66
  return f(*args, **kwargs)
67
+
62
68
  return wrapper
63
69
 
64
70
 
@@ -74,17 +80,21 @@ def require_pro(f):
74
80
  def pro_feature():
75
81
  ...
76
82
  """
83
+
77
84
  @functools.wraps(f)
78
85
  def wrapper(*args, **kwargs):
79
86
  from souleyez.auth import get_current_user, Tier
80
87
 
81
88
  user = get_current_user()
82
89
  if user is None or user.tier != Tier.PRO:
83
- click.echo(click.style("\n💎 Pro license required.", fg='yellow'))
84
- click.echo(click.style(" Upgrade at: cybersoulsecurity.com/upgrade", fg='cyan'))
90
+ click.echo(click.style("\n💎 Pro license required.", fg="yellow"))
91
+ click.echo(
92
+ click.style(" Upgrade at: cybersoulsecurity.com/upgrade", fg="cyan")
93
+ )
85
94
  sys.exit(1)
86
95
 
87
96
  return f(*args, **kwargs)
97
+
88
98
  return wrapper
89
99
 
90
100
 
@@ -106,21 +116,26 @@ def unlock_credentials_if_needed():
106
116
  return True
107
117
 
108
118
  # Need to unlock
109
- click.echo(click.style("🔒 Credentials are encrypted.", fg='yellow'))
119
+ click.echo(click.style("🔒 Credentials are encrypted.", fg="yellow"))
110
120
 
111
121
  max_attempts = 3
112
122
  for attempt in range(max_attempts):
113
123
  try:
114
124
  password = getpass.getpass("Enter master password: ")
115
125
  if crypto.unlock(password):
116
- click.echo(click.style("✅ Unlocked successfully!", fg='green'))
126
+ click.echo(click.style("✅ Unlocked successfully!", fg="green"))
117
127
  return True
118
128
  else:
119
129
  remaining = max_attempts - attempt - 1
120
130
  if remaining > 0:
121
- click.echo(click.style(f"❌ Incorrect password. {remaining} attempts remaining.", fg='red'))
131
+ click.echo(
132
+ click.style(
133
+ f"❌ Incorrect password. {remaining} attempts remaining.",
134
+ fg="red",
135
+ )
136
+ )
122
137
  else:
123
- click.echo(click.style("❌ Access denied.", fg='red'))
138
+ click.echo(click.style("❌ Access denied.", fg="red"))
124
139
  except KeyboardInterrupt:
125
140
  click.echo("\n❌ Cancelled by user.")
126
141
  return False
@@ -153,11 +168,22 @@ def require_password(f):
153
168
  Returns:
154
169
  The wrapped function that requires authentication
155
170
  """
171
+
156
172
  @functools.wraps(f)
157
173
  def wrapper(*args, **kwargs):
158
174
  if not unlock_credentials_if_needed():
159
- click.echo(click.style("\n⚠️ Authentication required to access this command.", fg='yellow'))
160
- click.echo(click.style(" This command accesses sensitive data and requires master password.", fg='yellow'))
175
+ click.echo(
176
+ click.style(
177
+ "\n⚠️ Authentication required to access this command.", fg="yellow"
178
+ )
179
+ )
180
+ click.echo(
181
+ click.style(
182
+ " This command accesses sensitive data and requires master password.",
183
+ fg="yellow",
184
+ )
185
+ )
161
186
  sys.exit(1)
162
187
  return f(*args, **kwargs)
188
+
163
189
  return wrapper