souleyez 2.43.29__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 +22783 -10678
  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.29.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.29.dist-info → souleyez-2.43.32.dist-info}/WHEEL +1 -1
  353. souleyez-2.43.29.dist-info/RECORD +0 -379
  354. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/entry_points.txt +0 -0
  355. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/licenses/LICENSE +0 -0
  356. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/top_level.txt +0 -0
@@ -8,10 +8,10 @@ from souleyez.storage.database import get_db
8
8
 
9
9
 
10
10
  # Valid status values
11
- STATUS_NOT_TRIED = 'not_tried'
12
- STATUS_ATTEMPTED = 'attempted'
13
- STATUS_FAILED = 'failed'
14
- STATUS_SUCCESS = 'success'
11
+ STATUS_NOT_TRIED = "not_tried"
12
+ STATUS_ATTEMPTED = "attempted"
13
+ STATUS_FAILED = "failed"
14
+ STATUS_SUCCESS = "success"
15
15
 
16
16
  VALID_STATUSES = [STATUS_NOT_TRIED, STATUS_ATTEMPTED, STATUS_FAILED, STATUS_SUCCESS]
17
17
 
@@ -23,7 +23,7 @@ def record_attempt(
23
23
  exploit_title: str,
24
24
  status: str,
25
25
  service_id: Optional[int] = None,
26
- notes: Optional[str] = None
26
+ notes: Optional[str] = None,
27
27
  ) -> int:
28
28
  """
29
29
  Record an exploit attempt. Creates new record or updates existing one.
@@ -47,15 +47,19 @@ def record_attempt(
47
47
  conn = db.get_connection()
48
48
 
49
49
  # Check if record exists
50
- existing = conn.execute("""
50
+ existing = conn.execute(
51
+ """
51
52
  SELECT id FROM exploit_attempts
52
53
  WHERE engagement_id = ? AND host_id = ? AND service_id IS ? AND exploit_identifier = ?
53
- """, (engagement_id, host_id, service_id, exploit_identifier)).fetchone()
54
+ """,
55
+ (engagement_id, host_id, service_id, exploit_identifier),
56
+ ).fetchone()
54
57
 
55
58
  if existing:
56
59
  # Update existing record
57
- attempt_id = existing['id']
58
- conn.execute("""
60
+ attempt_id = existing["id"]
61
+ conn.execute(
62
+ """
59
63
  UPDATE exploit_attempts
60
64
  SET status = ?,
61
65
  exploit_title = ?,
@@ -63,18 +67,29 @@ def record_attempt(
63
67
  attempted_at = ?,
64
68
  updated_at = CURRENT_TIMESTAMP
65
69
  WHERE id = ?
66
- """, (status, exploit_title, notes, datetime.now(), attempt_id))
70
+ """,
71
+ (status, exploit_title, notes, datetime.now(), attempt_id),
72
+ )
67
73
  else:
68
74
  # Create new record
69
- cursor = conn.execute("""
75
+ cursor = conn.execute(
76
+ """
70
77
  INSERT INTO exploit_attempts (
71
78
  engagement_id, host_id, service_id, exploit_identifier,
72
79
  exploit_title, status, attempted_at, notes
73
80
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
74
- """, (
75
- engagement_id, host_id, service_id, exploit_identifier,
76
- exploit_title, status, datetime.now() if status != STATUS_NOT_TRIED else None, notes
77
- ))
81
+ """,
82
+ (
83
+ engagement_id,
84
+ host_id,
85
+ service_id,
86
+ exploit_identifier,
87
+ exploit_title,
88
+ status,
89
+ datetime.now() if status != STATUS_NOT_TRIED else None,
90
+ notes,
91
+ ),
92
+ )
78
93
  attempt_id = cursor.lastrowid
79
94
 
80
95
  conn.commit()
@@ -87,7 +102,7 @@ def get_attempt_status(
87
102
  engagement_id: int,
88
103
  host_id: int,
89
104
  exploit_identifier: str,
90
- service_id: Optional[int] = None
105
+ service_id: Optional[int] = None,
91
106
  ) -> Optional[str]:
92
107
  """
93
108
  Get the status of a specific exploit attempt.
@@ -98,14 +113,17 @@ def get_attempt_status(
98
113
  db = get_db()
99
114
  conn = db.get_connection()
100
115
 
101
- result = conn.execute("""
116
+ result = conn.execute(
117
+ """
102
118
  SELECT status FROM exploit_attempts
103
119
  WHERE engagement_id = ? AND host_id = ? AND service_id IS ? AND exploit_identifier = ?
104
- """, (engagement_id, host_id, service_id, exploit_identifier)).fetchone()
120
+ """,
121
+ (engagement_id, host_id, service_id, exploit_identifier),
122
+ ).fetchone()
105
123
 
106
124
  conn.close()
107
125
 
108
- return result['status'] if result else None
126
+ return result["status"] if result else None
109
127
 
110
128
 
111
129
  def get_attempts_by_host(host_id: int) -> List[Dict[str, Any]]:
@@ -113,7 +131,8 @@ def get_attempts_by_host(host_id: int) -> List[Dict[str, Any]]:
113
131
  db = get_db()
114
132
  conn = db.get_connection()
115
133
 
116
- results = conn.execute("""
134
+ results = conn.execute(
135
+ """
117
136
  SELECT
118
137
  ea.*,
119
138
  s.port,
@@ -125,14 +144,18 @@ def get_attempts_by_host(host_id: int) -> List[Dict[str, Any]]:
125
144
  LEFT JOIN hosts h ON ea.host_id = h.id
126
145
  WHERE ea.host_id = ?
127
146
  ORDER BY ea.updated_at DESC
128
- """, (host_id,)).fetchall()
147
+ """,
148
+ (host_id,),
149
+ ).fetchall()
129
150
 
130
151
  conn.close()
131
152
 
132
153
  return [dict(r) for r in results]
133
154
 
134
155
 
135
- def get_attempts_by_engagement(engagement_id: int, status_filter: Optional[str] = None) -> List[Dict[str, Any]]:
156
+ def get_attempts_by_engagement(
157
+ engagement_id: int, status_filter: Optional[str] = None
158
+ ) -> List[Dict[str, Any]]:
136
159
  """
137
160
  Get all exploit attempts for an engagement, optionally filtered by status.
138
161
 
@@ -162,7 +185,9 @@ def get_attempts_by_engagement(engagement_id: int, status_filter: Optional[str]
162
185
 
163
186
  if status_filter:
164
187
  if status_filter not in VALID_STATUSES:
165
- raise ValueError(f"Invalid status filter. Must be one of: {', '.join(VALID_STATUSES)}")
188
+ raise ValueError(
189
+ f"Invalid status filter. Must be one of: {', '.join(VALID_STATUSES)}"
190
+ )
166
191
  query += " AND ea.status = ?"
167
192
  params.append(status_filter)
168
193
 
@@ -186,43 +211,43 @@ def get_attempt_stats(engagement_id: int) -> Dict[str, Any]:
186
211
  conn = db.get_connection()
187
212
 
188
213
  # Get status breakdown
189
- status_counts = conn.execute("""
214
+ status_counts = conn.execute(
215
+ """
190
216
  SELECT status, COUNT(*) as count
191
217
  FROM exploit_attempts
192
218
  WHERE engagement_id = ?
193
219
  GROUP BY status
194
- """, (engagement_id,)).fetchall()
220
+ """,
221
+ (engagement_id,),
222
+ ).fetchall()
195
223
 
196
- stats = {
197
- 'total': 0,
198
- 'not_tried': 0,
199
- 'attempted': 0,
200
- 'failed': 0,
201
- 'success': 0
202
- }
224
+ stats = {"total": 0, "not_tried": 0, "attempted": 0, "failed": 0, "success": 0}
203
225
 
204
226
  for row in status_counts:
205
- stats['total'] += row['count']
206
- stats[row['status']] = row['count']
227
+ stats["total"] += row["count"]
228
+ stats[row["status"]] = row["count"]
207
229
 
208
230
  # Get success rate if attempts have been made
209
- total_attempts = stats['attempted'] + stats['failed'] + stats['success']
231
+ total_attempts = stats["attempted"] + stats["failed"] + stats["success"]
210
232
  if total_attempts > 0:
211
- stats['success_rate'] = (stats['success'] / total_attempts) * 100
233
+ stats["success_rate"] = (stats["success"] / total_attempts) * 100
212
234
  else:
213
- stats['success_rate'] = 0
235
+ stats["success_rate"] = 0
214
236
 
215
237
  # Get host coverage
216
- host_coverage = conn.execute("""
238
+ host_coverage = conn.execute(
239
+ """
217
240
  SELECT
218
241
  COUNT(DISTINCT host_id) as hosts_with_attempts,
219
242
  (SELECT COUNT(*) FROM hosts WHERE engagement_id = ?) as total_hosts
220
243
  FROM exploit_attempts
221
244
  WHERE engagement_id = ?
222
- """, (engagement_id, engagement_id)).fetchone()
245
+ """,
246
+ (engagement_id, engagement_id),
247
+ ).fetchone()
223
248
 
224
- stats['hosts_with_attempts'] = host_coverage['hosts_with_attempts']
225
- stats['total_hosts'] = host_coverage['total_hosts']
249
+ stats["hosts_with_attempts"] = host_coverage["hosts_with_attempts"]
250
+ stats["total_hosts"] = host_coverage["total_hosts"]
226
251
 
227
252
  conn.close()
228
253
 
@@ -230,9 +255,7 @@ def get_attempt_stats(engagement_id: int) -> Dict[str, Any]:
230
255
 
231
256
 
232
257
  def update_attempt_status(
233
- attempt_id: int,
234
- status: str,
235
- notes: Optional[str] = None
258
+ attempt_id: int, status: str, notes: Optional[str] = None
236
259
  ) -> bool:
237
260
  """
238
261
  Update the status of an existing attempt.
@@ -251,14 +274,22 @@ def update_attempt_status(
251
274
  db = get_db()
252
275
  conn = db.get_connection()
253
276
 
254
- cursor = conn.execute("""
277
+ cursor = conn.execute(
278
+ """
255
279
  UPDATE exploit_attempts
256
280
  SET status = ?,
257
281
  notes = ?,
258
282
  attempted_at = ?,
259
283
  updated_at = CURRENT_TIMESTAMP
260
284
  WHERE id = ?
261
- """, (status, notes, datetime.now() if status != STATUS_NOT_TRIED else None, attempt_id))
285
+ """,
286
+ (
287
+ status,
288
+ notes,
289
+ datetime.now() if status != STATUS_NOT_TRIED else None,
290
+ attempt_id,
291
+ ),
292
+ )
262
293
 
263
294
  rows_updated = cursor.rowcount
264
295
 
@@ -287,7 +318,9 @@ def delete_attempts_by_engagement(engagement_id: int) -> int:
287
318
  db = get_db()
288
319
  conn = db.get_connection()
289
320
 
290
- cursor = conn.execute("DELETE FROM exploit_attempts WHERE engagement_id = ?", (engagement_id,))
321
+ cursor = conn.execute(
322
+ "DELETE FROM exploit_attempts WHERE engagement_id = ?", (engagement_id,)
323
+ )
291
324
  rows_deleted = cursor.rowcount
292
325
 
293
326
  conn.commit()
@@ -327,13 +360,13 @@ def bulk_record_attempts(attempts: List[Dict[str, Any]]) -> int:
327
360
  count = 0
328
361
  for attempt in attempts:
329
362
  record_attempt(
330
- engagement_id=attempt['engagement_id'],
331
- host_id=attempt['host_id'],
332
- exploit_identifier=attempt['exploit_identifier'],
333
- exploit_title=attempt['exploit_title'],
334
- status=attempt['status'],
335
- service_id=attempt.get('service_id'),
336
- notes=attempt.get('notes')
363
+ engagement_id=attempt["engagement_id"],
364
+ host_id=attempt["host_id"],
365
+ exploit_identifier=attempt["exploit_identifier"],
366
+ exploit_title=attempt["exploit_title"],
367
+ status=attempt["status"],
368
+ service_id=attempt.get("service_id"),
369
+ notes=attempt.get("notes"),
337
370
  )
338
371
  count += 1
339
372
 
@@ -352,12 +385,15 @@ def get_untried_exploits_for_host(engagement_id: int, host_id: int) -> List[str]
352
385
  db = get_db()
353
386
  conn = db.get_connection()
354
387
 
355
- results = conn.execute("""
388
+ results = conn.execute(
389
+ """
356
390
  SELECT exploit_identifier
357
391
  FROM exploit_attempts
358
392
  WHERE engagement_id = ? AND host_id = ? AND status = ?
359
- """, (engagement_id, host_id, STATUS_NOT_TRIED)).fetchall()
393
+ """,
394
+ (engagement_id, host_id, STATUS_NOT_TRIED),
395
+ ).fetchall()
360
396
 
361
397
  conn.close()
362
398
 
363
- return [r['exploit_identifier'] for r in results]
399
+ return [r["exploit_identifier"] for r in results]
@@ -15,26 +15,36 @@ def add_exploit(
15
15
  url: str,
16
16
  date_published: str,
17
17
  search_term: str,
18
- service_id: Optional[int] = None
18
+ service_id: Optional[int] = None,
19
19
  ) -> int:
20
20
  """Add a new exploit to the database."""
21
21
  db = get_db()
22
22
  conn = db.get_connection()
23
-
24
- cursor = conn.execute("""
23
+
24
+ cursor = conn.execute(
25
+ """
25
26
  INSERT INTO exploits (
26
27
  engagement_id, service_id, edb_id, title, platform,
27
28
  type, url, date_published, search_term
28
29
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
29
- """, (
30
- engagement_id, service_id, edb_id, title, platform,
31
- exploit_type, url, date_published, search_term
32
- ))
33
-
30
+ """,
31
+ (
32
+ engagement_id,
33
+ service_id,
34
+ edb_id,
35
+ title,
36
+ platform,
37
+ exploit_type,
38
+ url,
39
+ date_published,
40
+ search_term,
41
+ ),
42
+ )
43
+
34
44
  exploit_id = cursor.lastrowid
35
45
  conn.commit()
36
46
  conn.close()
37
-
47
+
38
48
  return exploit_id
39
49
 
40
50
 
@@ -42,8 +52,9 @@ def get_exploits_by_engagement(engagement_id: int) -> List[Dict[str, Any]]:
42
52
  """Get all exploits for an engagement."""
43
53
  db = get_db()
44
54
  conn = db.get_connection()
45
-
46
- results = conn.execute("""
55
+
56
+ results = conn.execute(
57
+ """
47
58
  SELECT
48
59
  e.*,
49
60
  s.port,
@@ -55,10 +66,12 @@ def get_exploits_by_engagement(engagement_id: int) -> List[Dict[str, Any]]:
55
66
  LEFT JOIN hosts h ON s.host_id = h.id
56
67
  WHERE e.engagement_id = ?
57
68
  ORDER BY e.found_at DESC
58
- """, (engagement_id,)).fetchall()
59
-
69
+ """,
70
+ (engagement_id,),
71
+ ).fetchall()
72
+
60
73
  conn.close()
61
-
74
+
62
75
  return [dict(r) for r in results]
63
76
 
64
77
 
@@ -66,15 +79,18 @@ def get_exploits_by_service(service_id: int) -> List[Dict[str, Any]]:
66
79
  """Get all exploits for a specific service."""
67
80
  db = get_db()
68
81
  conn = db.get_connection()
69
-
70
- results = conn.execute("""
82
+
83
+ results = conn.execute(
84
+ """
71
85
  SELECT * FROM exploits
72
86
  WHERE service_id = ?
73
87
  ORDER BY found_at DESC
74
- """, (service_id,)).fetchall()
75
-
88
+ """,
89
+ (service_id,),
90
+ ).fetchall()
91
+
76
92
  conn.close()
77
-
93
+
78
94
  return [dict(r) for r in results]
79
95
 
80
96
 
@@ -84,7 +100,8 @@ def get_exploit_stats(engagement_id: int) -> Dict[str, Any]:
84
100
  conn = db.get_connection()
85
101
 
86
102
  # Get basic stats
87
- stats = conn.execute("""
103
+ stats = conn.execute(
104
+ """
88
105
  SELECT
89
106
  COUNT(*) as total,
90
107
  COUNT(DISTINCT platform) as platforms,
@@ -92,36 +109,48 @@ def get_exploit_stats(engagement_id: int) -> Dict[str, Any]:
92
109
  COUNT(DISTINCT service_id) as services_with_exploits
93
110
  FROM exploits
94
111
  WHERE engagement_id = ?
95
- """, (engagement_id,)).fetchone()
112
+ """,
113
+ (engagement_id,),
114
+ ).fetchone()
96
115
 
97
- result = dict(stats) if stats else {
98
- 'total': 0,
99
- 'platforms': 0,
100
- 'types': 0,
101
- 'services_with_exploits': 0
102
- }
116
+ result = (
117
+ dict(stats)
118
+ if stats
119
+ else {"total": 0, "platforms": 0, "types": 0, "services_with_exploits": 0}
120
+ )
103
121
 
104
122
  # Get platform breakdown (top 5)
105
- platform_breakdown = conn.execute("""
123
+ platform_breakdown = conn.execute(
124
+ """
106
125
  SELECT platform, COUNT(*) as count
107
126
  FROM exploits
108
127
  WHERE engagement_id = ?
109
128
  GROUP BY platform
110
129
  ORDER BY count DESC
111
130
  LIMIT 5
112
- """, (engagement_id,)).fetchall()
113
- result['platform_breakdown'] = [{'platform': row['platform'], 'count': row['count']} for row in platform_breakdown]
131
+ """,
132
+ (engagement_id,),
133
+ ).fetchall()
134
+ result["platform_breakdown"] = [
135
+ {"platform": row["platform"], "count": row["count"]}
136
+ for row in platform_breakdown
137
+ ]
114
138
 
115
139
  # Get type breakdown (top 5)
116
- type_breakdown = conn.execute("""
140
+ type_breakdown = conn.execute(
141
+ """
117
142
  SELECT type, COUNT(*) as count
118
143
  FROM exploits
119
144
  WHERE engagement_id = ?
120
145
  GROUP BY type
121
146
  ORDER BY count DESC
122
147
  LIMIT 5
123
- """, (engagement_id,)).fetchall()
124
- result['type_breakdown'] = [{'type': row['type'], 'count': row['count']} for row in type_breakdown]
148
+ """,
149
+ (engagement_id,),
150
+ ).fetchall()
151
+ result["type_breakdown"] = [
152
+ {"type": row["type"], "count": row["count"]} for row in type_breakdown
153
+ ]
125
154
 
126
155
  conn.close()
127
156
 
@@ -147,7 +176,9 @@ def delete_exploits_by_engagement(engagement_id: int) -> int:
147
176
  db = get_db()
148
177
  conn = db.get_connection()
149
178
 
150
- cursor = conn.execute("DELETE FROM exploits WHERE engagement_id = ?", (engagement_id,))
179
+ cursor = conn.execute(
180
+ "DELETE FROM exploits WHERE engagement_id = ?", (engagement_id,)
181
+ )
151
182
  rows_deleted = cursor.rowcount
152
183
 
153
184
  conn.commit()
@@ -164,7 +195,7 @@ def delete_exploits_by_ids(exploit_ids: List[int]) -> int:
164
195
  db = get_db()
165
196
  conn = db.get_connection()
166
197
 
167
- placeholders = ','.join('?' * len(exploit_ids))
198
+ placeholders = ",".join("?" * len(exploit_ids))
168
199
  query = f"DELETE FROM exploits WHERE id IN ({placeholders})"
169
200
 
170
201
  cursor = conn.execute(query, exploit_ids)
@@ -210,7 +241,7 @@ def search_exploits(engagement_id: int, search_term: str) -> List[Dict[str, Any]
210
241
  )
211
242
  """
212
243
  # Add three parameters (one for each field) for this keyword
213
- keyword_pattern = f'%{keyword}%'
244
+ keyword_pattern = f"%{keyword}%"
214
245
  params.extend([keyword_pattern, keyword_pattern, keyword_pattern])
215
246
 
216
247
  query += "ORDER BY e.found_at DESC"