souleyez 2.43.29__py3-none-any.whl → 3.0.0__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 (358) 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 +9564 -2881
  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 +1239 -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 +2200 -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 +564 -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 +409 -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 +417 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +292 -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/web_login_test_handler.py +327 -0
  126. souleyez/handlers/whois_handler.py +277 -0
  127. souleyez/handlers/wpscan_handler.py +554 -0
  128. souleyez/history.py +32 -16
  129. souleyez/importers/msf_importer.py +106 -75
  130. souleyez/importers/smart_importer.py +208 -147
  131. souleyez/integrations/siem/__init__.py +10 -10
  132. souleyez/integrations/siem/base.py +17 -18
  133. souleyez/integrations/siem/elastic.py +108 -122
  134. souleyez/integrations/siem/factory.py +207 -80
  135. souleyez/integrations/siem/googlesecops.py +146 -154
  136. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  137. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  138. souleyez/integrations/siem/sentinel.py +107 -109
  139. souleyez/integrations/siem/splunk.py +246 -212
  140. souleyez/integrations/siem/wazuh.py +65 -71
  141. souleyez/integrations/wazuh/__init__.py +5 -5
  142. souleyez/integrations/wazuh/client.py +70 -93
  143. souleyez/integrations/wazuh/config.py +85 -57
  144. souleyez/integrations/wazuh/host_mapper.py +28 -36
  145. souleyez/integrations/wazuh/sync.py +78 -68
  146. souleyez/intelligence/__init__.py +4 -5
  147. souleyez/intelligence/correlation_analyzer.py +309 -295
  148. souleyez/intelligence/exploit_knowledge.py +661 -623
  149. souleyez/intelligence/exploit_suggestions.py +159 -139
  150. souleyez/intelligence/gap_analyzer.py +132 -97
  151. souleyez/intelligence/gap_detector.py +251 -214
  152. souleyez/intelligence/sensitive_tables.py +266 -129
  153. souleyez/intelligence/service_parser.py +137 -123
  154. souleyez/intelligence/surface_analyzer.py +407 -268
  155. souleyez/intelligence/target_parser.py +159 -162
  156. souleyez/licensing/__init__.py +6 -6
  157. souleyez/licensing/validator.py +17 -19
  158. souleyez/log_config.py +79 -54
  159. souleyez/main.py +1505 -687
  160. souleyez/migrations/fix_job_counter.py +16 -14
  161. souleyez/parsers/bloodhound_parser.py +41 -39
  162. souleyez/parsers/crackmapexec_parser.py +178 -111
  163. souleyez/parsers/dalfox_parser.py +72 -77
  164. souleyez/parsers/dnsrecon_parser.py +103 -91
  165. souleyez/parsers/enum4linux_parser.py +183 -153
  166. souleyez/parsers/ffuf_parser.py +29 -25
  167. souleyez/parsers/gobuster_parser.py +301 -41
  168. souleyez/parsers/hashcat_parser.py +324 -79
  169. souleyez/parsers/http_fingerprint_parser.py +350 -103
  170. souleyez/parsers/hydra_parser.py +131 -111
  171. souleyez/parsers/impacket_parser.py +231 -178
  172. souleyez/parsers/john_parser.py +98 -86
  173. souleyez/parsers/katana_parser.py +316 -0
  174. souleyez/parsers/msf_parser.py +943 -498
  175. souleyez/parsers/nikto_parser.py +346 -65
  176. souleyez/parsers/nmap_parser.py +262 -174
  177. souleyez/parsers/nuclei_parser.py +40 -44
  178. souleyez/parsers/responder_parser.py +26 -26
  179. souleyez/parsers/searchsploit_parser.py +74 -74
  180. souleyez/parsers/service_explorer_parser.py +279 -0
  181. souleyez/parsers/smbmap_parser.py +180 -124
  182. souleyez/parsers/sqlmap_parser.py +434 -308
  183. souleyez/parsers/theharvester_parser.py +75 -57
  184. souleyez/parsers/whois_parser.py +135 -94
  185. souleyez/parsers/wpscan_parser.py +278 -190
  186. souleyez/plugins/afp.py +44 -36
  187. souleyez/plugins/afp_brute.py +114 -46
  188. souleyez/plugins/ard.py +48 -37
  189. souleyez/plugins/bloodhound.py +95 -61
  190. souleyez/plugins/certipy.py +303 -0
  191. souleyez/plugins/crackmapexec.py +186 -85
  192. souleyez/plugins/dalfox.py +120 -59
  193. souleyez/plugins/dns_hijack.py +146 -41
  194. souleyez/plugins/dnsrecon.py +97 -61
  195. souleyez/plugins/enum4linux.py +91 -66
  196. souleyez/plugins/evil_winrm.py +291 -0
  197. souleyez/plugins/ffuf.py +166 -90
  198. souleyez/plugins/firmware_extract.py +133 -29
  199. souleyez/plugins/gobuster.py +387 -190
  200. souleyez/plugins/gpp_extract.py +393 -0
  201. souleyez/plugins/hashcat.py +100 -73
  202. souleyez/plugins/http_fingerprint.py +913 -267
  203. souleyez/plugins/hydra.py +566 -200
  204. souleyez/plugins/impacket_getnpusers.py +117 -69
  205. souleyez/plugins/impacket_psexec.py +84 -64
  206. souleyez/plugins/impacket_secretsdump.py +103 -69
  207. souleyez/plugins/impacket_smbclient.py +89 -75
  208. souleyez/plugins/john.py +86 -69
  209. souleyez/plugins/katana.py +313 -0
  210. souleyez/plugins/kerbrute.py +237 -0
  211. souleyez/plugins/lfi_extract.py +541 -0
  212. souleyez/plugins/macos_ssh.py +117 -48
  213. souleyez/plugins/mdns.py +35 -30
  214. souleyez/plugins/msf_auxiliary.py +253 -130
  215. souleyez/plugins/msf_exploit.py +239 -161
  216. souleyez/plugins/nikto.py +134 -78
  217. souleyez/plugins/nmap.py +275 -91
  218. souleyez/plugins/nuclei.py +180 -89
  219. souleyez/plugins/nxc.py +285 -0
  220. souleyez/plugins/plugin_base.py +35 -36
  221. souleyez/plugins/plugin_template.py +13 -5
  222. souleyez/plugins/rdp_sec_check.py +130 -0
  223. souleyez/plugins/responder.py +112 -71
  224. souleyez/plugins/router_http_brute.py +76 -65
  225. souleyez/plugins/router_ssh_brute.py +118 -41
  226. souleyez/plugins/router_telnet_brute.py +124 -42
  227. souleyez/plugins/routersploit.py +91 -59
  228. souleyez/plugins/routersploit_exploit.py +77 -55
  229. souleyez/plugins/searchsploit.py +91 -77
  230. souleyez/plugins/service_explorer.py +1160 -0
  231. souleyez/plugins/smbmap.py +122 -72
  232. souleyez/plugins/smbpasswd.py +215 -0
  233. souleyez/plugins/sqlmap.py +301 -113
  234. souleyez/plugins/theharvester.py +127 -75
  235. souleyez/plugins/tr069.py +79 -57
  236. souleyez/plugins/upnp.py +65 -47
  237. souleyez/plugins/upnp_abuse.py +73 -55
  238. souleyez/plugins/vnc_access.py +129 -42
  239. souleyez/plugins/vnc_brute.py +109 -38
  240. souleyez/plugins/web_login_test.py +417 -0
  241. souleyez/plugins/whois.py +77 -58
  242. souleyez/plugins/wpscan.py +219 -69
  243. souleyez/reporting/__init__.py +2 -1
  244. souleyez/reporting/attack_chain.py +411 -346
  245. souleyez/reporting/charts.py +436 -501
  246. souleyez/reporting/compliance_mappings.py +334 -201
  247. souleyez/reporting/detection_report.py +126 -125
  248. souleyez/reporting/formatters.py +828 -591
  249. souleyez/reporting/generator.py +386 -302
  250. souleyez/reporting/metrics.py +72 -75
  251. souleyez/scanner.py +35 -29
  252. souleyez/security/__init__.py +37 -11
  253. souleyez/security/scope_validator.py +175 -106
  254. souleyez/security/validation.py +237 -149
  255. souleyez/security.py +22 -6
  256. souleyez/storage/credentials.py +247 -186
  257. souleyez/storage/crypto.py +296 -129
  258. souleyez/storage/database.py +73 -50
  259. souleyez/storage/db.py +58 -36
  260. souleyez/storage/deliverable_evidence.py +177 -128
  261. souleyez/storage/deliverable_exporter.py +282 -246
  262. souleyez/storage/deliverable_templates.py +134 -116
  263. souleyez/storage/deliverables.py +135 -130
  264. souleyez/storage/engagements.py +109 -56
  265. souleyez/storage/evidence.py +181 -152
  266. souleyez/storage/execution_log.py +31 -17
  267. souleyez/storage/exploit_attempts.py +93 -57
  268. souleyez/storage/exploits.py +67 -36
  269. souleyez/storage/findings.py +48 -61
  270. souleyez/storage/hosts.py +176 -144
  271. souleyez/storage/migrate_to_engagements.py +43 -19
  272. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  273. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  274. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  275. souleyez/storage/migrations/_005_screenshots.py +13 -5
  276. souleyez/storage/migrations/_006_deliverables.py +13 -5
  277. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  278. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  279. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  280. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  281. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  282. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  283. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  284. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  285. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  286. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  287. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  288. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  289. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  290. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  291. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  292. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  293. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  294. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  295. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  296. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  297. souleyez/storage/migrations/__init__.py +26 -26
  298. souleyez/storage/migrations/migration_manager.py +19 -19
  299. souleyez/storage/msf_sessions.py +100 -65
  300. souleyez/storage/osint.py +17 -24
  301. souleyez/storage/recommendation_engine.py +269 -235
  302. souleyez/storage/screenshots.py +33 -32
  303. souleyez/storage/smb_shares.py +136 -92
  304. souleyez/storage/sqlmap_data.py +183 -128
  305. souleyez/storage/team_collaboration.py +135 -141
  306. souleyez/storage/timeline_tracker.py +122 -94
  307. souleyez/storage/wazuh_vulns.py +64 -66
  308. souleyez/storage/web_paths.py +33 -37
  309. souleyez/testing/credential_tester.py +221 -205
  310. souleyez/ui/__init__.py +1 -1
  311. souleyez/ui/ai_quotes.py +12 -12
  312. souleyez/ui/attack_surface.py +2439 -1516
  313. souleyez/ui/chain_rules_view.py +914 -382
  314. souleyez/ui/correlation_view.py +312 -230
  315. souleyez/ui/dashboard.py +2382 -1130
  316. souleyez/ui/deliverables_view.py +148 -62
  317. souleyez/ui/design_system.py +13 -13
  318. souleyez/ui/errors.py +49 -49
  319. souleyez/ui/evidence_linking_view.py +284 -179
  320. souleyez/ui/evidence_vault.py +393 -285
  321. souleyez/ui/exploit_suggestions_view.py +555 -349
  322. souleyez/ui/export_view.py +100 -66
  323. souleyez/ui/gap_analysis_view.py +315 -171
  324. souleyez/ui/help_system.py +105 -97
  325. souleyez/ui/intelligence_view.py +436 -293
  326. souleyez/ui/interactive.py +23034 -10679
  327. souleyez/ui/interactive_selector.py +75 -68
  328. souleyez/ui/log_formatter.py +47 -39
  329. souleyez/ui/menu_components.py +22 -13
  330. souleyez/ui/msf_auxiliary_menu.py +184 -133
  331. souleyez/ui/pending_chains_view.py +336 -172
  332. souleyez/ui/progress_indicators.py +5 -3
  333. souleyez/ui/recommendations_view.py +195 -137
  334. souleyez/ui/rule_builder.py +343 -225
  335. souleyez/ui/setup_wizard.py +678 -284
  336. souleyez/ui/shortcuts.py +217 -165
  337. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  338. souleyez/ui/splunk_vulns_view.py +139 -86
  339. souleyez/ui/team_dashboard.py +498 -335
  340. souleyez/ui/template_selector.py +196 -105
  341. souleyez/ui/terminal.py +6 -6
  342. souleyez/ui/timeline_view.py +198 -127
  343. souleyez/ui/tool_setup.py +264 -164
  344. souleyez/ui/tutorial.py +202 -72
  345. souleyez/ui/tutorial_state.py +40 -40
  346. souleyez/ui/wazuh_vulns_view.py +235 -141
  347. souleyez/ui/wordlist_browser.py +260 -107
  348. souleyez/ui.py +464 -312
  349. souleyez/utils/tool_checker.py +427 -367
  350. souleyez/utils.py +33 -29
  351. souleyez/wordlists.py +134 -167
  352. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
  353. souleyez-3.0.0.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-3.0.0.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