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
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
14
14
  class FeedbackHandler:
15
15
  """
16
16
  Automatically update database based on command execution results.
17
-
17
+
18
18
  Implements the feedback loop:
19
19
  - Update credential status (valid/invalid)
20
20
  - Update host status (compromised/active)
@@ -32,63 +32,74 @@ class FeedbackHandler:
32
32
  engagement_id: int,
33
33
  parsed_result: Dict[str, Any],
34
34
  recommendation: Dict[str, Any],
35
- command: str
35
+ command: str,
36
36
  ) -> Dict[str, Any]:
37
37
  """
38
38
  Apply feedback updates to database.
39
-
39
+
40
40
  Args:
41
41
  engagement_id: Current engagement ID
42
42
  parsed_result: Parsed command result
43
43
  recommendation: Original AI recommendation
44
44
  command: Command that was executed
45
-
45
+
46
46
  Returns:
47
47
  Dict describing what was updated
48
48
  """
49
49
  feedback = {
50
- 'hosts_updated': 0,
51
- 'credentials_updated': 0,
52
- 'services_added': 0,
53
- 'notes_added': []
50
+ "hosts_updated": 0,
51
+ "credentials_updated": 0,
52
+ "services_added": 0,
53
+ "notes_added": [],
54
54
  }
55
-
55
+
56
56
  # Extract target IP
57
- target = recommendation.get('target', '')
58
- action = recommendation.get('action', '').lower()
59
-
57
+ target = recommendation.get("target", "")
58
+ action = recommendation.get("action", "").lower()
59
+
60
60
  import re
61
- ip_match = re.search(r'\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b', target)
61
+
62
+ ip_match = re.search(r"\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b", target)
62
63
  if not ip_match:
63
64
  logger.warning(f"Could not extract IP from target: {target}")
64
65
  return feedback
65
-
66
+
66
67
  ip = ip_match.group(1)
67
-
68
+
68
69
  # Get host from database
69
70
  host = self.host_mgr.get_host_by_ip(engagement_id, ip)
70
71
  if not host:
71
72
  logger.warning(f"Host {ip} not found in engagement {engagement_id}")
72
73
  return feedback
73
-
74
+
74
75
  # Handle SSH results
75
- if 'ssh' in command.lower() and parsed_result.get('credential_valid') is not None:
76
- feedback.update(self._handle_ssh_feedback(
77
- engagement_id, host, parsed_result, recommendation
78
- ))
79
-
76
+ if (
77
+ "ssh" in command.lower()
78
+ and parsed_result.get("credential_valid") is not None
79
+ ):
80
+ feedback.update(
81
+ self._handle_ssh_feedback(
82
+ engagement_id, host, parsed_result, recommendation
83
+ )
84
+ )
85
+
80
86
  # Handle MySQL results
81
- elif 'mysql' in command.lower() and parsed_result.get('credential_valid') is not None:
82
- feedback.update(self._handle_mysql_feedback(
83
- engagement_id, host, parsed_result, recommendation
84
- ))
85
-
87
+ elif (
88
+ "mysql" in command.lower()
89
+ and parsed_result.get("credential_valid") is not None
90
+ ):
91
+ feedback.update(
92
+ self._handle_mysql_feedback(
93
+ engagement_id, host, parsed_result, recommendation
94
+ )
95
+ )
96
+
86
97
  # Handle nmap results
87
- elif 'nmap' in command.lower() and parsed_result.get('open_ports'):
88
- feedback.update(self._handle_nmap_feedback(
89
- engagement_id, host, parsed_result
90
- ))
91
-
98
+ elif "nmap" in command.lower() and parsed_result.get("open_ports"):
99
+ feedback.update(
100
+ self._handle_nmap_feedback(engagement_id, host, parsed_result)
101
+ )
102
+
92
103
  return feedback
93
104
 
94
105
  def _handle_ssh_feedback(
@@ -96,61 +107,63 @@ class FeedbackHandler:
96
107
  engagement_id: int,
97
108
  host: Dict[str, Any],
98
109
  result: Dict[str, Any],
99
- recommendation: Dict[str, Any]
110
+ recommendation: Dict[str, Any],
100
111
  ) -> Dict[str, Any]:
101
112
  """Handle feedback for SSH credential testing."""
102
- feedback = {'hosts_updated': 0, 'credentials_updated': 0, 'notes_added': []}
103
-
113
+ feedback = {"hosts_updated": 0, "credentials_updated": 0, "notes_added": []}
114
+
104
115
  # Extract credentials from recommendation
105
116
  import re
106
- text = f"{recommendation.get('action', '')} {recommendation.get('rationale', '')}"
107
- cred_match = re.search(r'(\w+):(\w+)', text)
108
-
117
+
118
+ text = (
119
+ f"{recommendation.get('action', '')} {recommendation.get('rationale', '')}"
120
+ )
121
+ cred_match = re.search(r"(\w+):(\w+)", text)
122
+
109
123
  if not cred_match:
110
124
  logger.warning("Could not extract credentials from recommendation")
111
125
  return feedback
112
-
126
+
113
127
  username = cred_match.group(1)
114
128
  password = cred_match.group(2)
115
-
129
+
116
130
  # Find credential in database
117
131
  creds = self.creds_mgr.list_credentials(engagement_id)
118
132
  cred_id = None
119
133
  for cred in creds:
120
- if cred.get('username') == username and cred.get('password') == password:
121
- cred_id = cred.get('id')
134
+ if cred.get("username") == username and cred.get("password") == password:
135
+ cred_id = cred.get("id")
122
136
  break
123
-
137
+
124
138
  # Update credential status
125
139
  if cred_id:
126
- status = 'valid' if result.get('credential_valid') else 'invalid'
127
- notes = result.get('details', '')
128
-
129
- self.creds_mgr.update_credential_status(
130
- cred_id,
131
- status=status,
132
- notes=notes
140
+ status = "valid" if result.get("credential_valid") else "invalid"
141
+ notes = result.get("details", "")
142
+
143
+ self.creds_mgr.update_credential_status(cred_id, status=status, notes=notes)
144
+ feedback["credentials_updated"] = 1
145
+ feedback["notes_added"].append(
146
+ f"Updated credential {username} status: {status}"
133
147
  )
134
- feedback['credentials_updated'] = 1
135
- feedback['notes_added'].append(f"Updated credential {username} status: {status}")
136
148
  logger.info(f"Updated credential {cred_id} status to {status}")
137
-
149
+
138
150
  # Update host status if credentials were valid
139
- if result.get('credential_valid'):
140
- access_level = result.get('access_level', 'user')
141
- status = 'compromised'
151
+ if result.get("credential_valid"):
152
+ access_level = result.get("access_level", "user")
153
+ status = "compromised"
142
154
  notes = f"Gained {access_level} access via SSH with {username}:{password}"
143
-
155
+
144
156
  self.host_mgr.update_host_status(
145
- host['id'],
146
- status=status,
147
- access_level=access_level,
148
- notes=notes
157
+ host["id"], status=status, access_level=access_level, notes=notes
158
+ )
159
+ feedback["hosts_updated"] = 1
160
+ feedback["notes_added"].append(
161
+ f"Updated host {host['ip_address']}: {status}, access={access_level}"
149
162
  )
150
- feedback['hosts_updated'] = 1
151
- feedback['notes_added'].append(f"Updated host {host['ip_address']}: {status}, access={access_level}")
152
- logger.info(f"Updated host {host['id']} to {status} with {access_level} access")
153
-
163
+ logger.info(
164
+ f"Updated host {host['id']} to {status} with {access_level} access"
165
+ )
166
+
154
167
  return feedback
155
168
 
156
169
  def _handle_mysql_feedback(
@@ -158,106 +171,108 @@ class FeedbackHandler:
158
171
  engagement_id: int,
159
172
  host: Dict[str, Any],
160
173
  result: Dict[str, Any],
161
- recommendation: Dict[str, Any]
174
+ recommendation: Dict[str, Any],
162
175
  ) -> Dict[str, Any]:
163
176
  """Handle feedback for MySQL credential testing."""
164
- feedback = {'hosts_updated': 0, 'credentials_updated': 0, 'notes_added': []}
165
-
177
+ feedback = {"hosts_updated": 0, "credentials_updated": 0, "notes_added": []}
178
+
166
179
  # Extract credentials
167
180
  import re
168
- text = f"{recommendation.get('action', '')} {recommendation.get('rationale', '')}"
169
- cred_match = re.search(r'(\w+):(\w+)', text)
170
-
181
+
182
+ text = (
183
+ f"{recommendation.get('action', '')} {recommendation.get('rationale', '')}"
184
+ )
185
+ cred_match = re.search(r"(\w+):(\w+)", text)
186
+
171
187
  if not cred_match:
172
188
  return feedback
173
-
189
+
174
190
  username = cred_match.group(1)
175
191
  password = cred_match.group(2)
176
-
192
+
177
193
  # Find/update credential
178
194
  creds = self.creds_mgr.list_credentials(engagement_id)
179
195
  cred_id = None
180
196
  for cred in creds:
181
- if (cred.get('username') == username and
182
- cred.get('password') == password and
183
- cred.get('service') == 'mysql'):
184
- cred_id = cred.get('id')
197
+ if (
198
+ cred.get("username") == username
199
+ and cred.get("password") == password
200
+ and cred.get("service") == "mysql"
201
+ ):
202
+ cred_id = cred.get("id")
185
203
  break
186
-
204
+
187
205
  if cred_id:
188
- status = 'valid' if result.get('credential_valid') else 'invalid'
189
- notes = result.get('details', '')
190
-
191
- self.creds_mgr.update_credential_status(
192
- cred_id,
193
- status=status,
194
- notes=notes
206
+ status = "valid" if result.get("credential_valid") else "invalid"
207
+ notes = result.get("details", "")
208
+
209
+ self.creds_mgr.update_credential_status(cred_id, status=status, notes=notes)
210
+ feedback["credentials_updated"] = 1
211
+ feedback["notes_added"].append(
212
+ f"Updated MySQL credential {username} status: {status}"
195
213
  )
196
- feedback['credentials_updated'] = 1
197
- feedback['notes_added'].append(f"Updated MySQL credential {username} status: {status}")
198
-
214
+
199
215
  # Update host notes if connection was successful
200
- if result.get('credential_valid'):
216
+ if result.get("credential_valid"):
201
217
  notes = f"MySQL access confirmed with {username}:{password}"
202
- if result.get('databases'):
218
+ if result.get("databases"):
203
219
  notes += f" - Databases: {', '.join(result['databases'][:5])}"
204
-
220
+
205
221
  # Only update if not already compromised with higher access
206
- current_access = host.get('access_level', 'none')
207
- if current_access == 'none':
208
- self.host_mgr.update_host_status(
209
- host['id'],
210
- notes=notes
222
+ current_access = host.get("access_level", "none")
223
+ if current_access == "none":
224
+ self.host_mgr.update_host_status(host["id"], notes=notes)
225
+ feedback["hosts_updated"] = 1
226
+ feedback["notes_added"].append(
227
+ f"Added MySQL access notes to host {host['ip_address']}"
211
228
  )
212
- feedback['hosts_updated'] = 1
213
- feedback['notes_added'].append(f"Added MySQL access notes to host {host['ip_address']}")
214
-
229
+
215
230
  return feedback
216
231
 
217
232
  def _handle_nmap_feedback(
218
- self,
219
- engagement_id: int,
220
- host: Dict[str, Any],
221
- result: Dict[str, Any]
233
+ self, engagement_id: int, host: Dict[str, Any], result: Dict[str, Any]
222
234
  ) -> Dict[str, Any]:
223
235
  """Handle feedback for nmap scans."""
224
- feedback = {'hosts_updated': 0, 'services_added': 0, 'notes_added': []}
225
-
236
+ feedback = {"hosts_updated": 0, "services_added": 0, "notes_added": []}
237
+
226
238
  # Add discovered services
227
- open_ports = result.get('open_ports', [])
228
- services_info = result.get('services', {})
229
-
239
+ open_ports = result.get("open_ports", [])
240
+ services_info = result.get("services", {})
241
+
230
242
  for port in open_ports:
231
- service_info = services_info.get(str(port), 'unknown')
232
-
243
+ service_info = services_info.get(str(port), "unknown")
244
+
233
245
  # Try to parse service name and version
234
246
  import re
235
- service_match = re.match(r'(\S+)(?:\s+(.+))?', service_info)
247
+
248
+ service_match = re.match(r"(\S+)(?:\s+(.+))?", service_info)
236
249
  if service_match:
237
250
  service_name = service_match.group(1)
238
- service_version = service_match.group(2) if service_match.group(2) else None
251
+ service_version = (
252
+ service_match.group(2) if service_match.group(2) else None
253
+ )
239
254
  else:
240
- service_name = 'unknown'
255
+ service_name = "unknown"
241
256
  service_version = None
242
-
257
+
243
258
  # Add service to database
244
259
  try:
245
260
  self.host_mgr.add_service(
246
- host['id'],
261
+ host["id"],
247
262
  port=port,
248
- protocol='tcp',
263
+ protocol="tcp",
249
264
  service_name=service_name,
250
265
  service_version=service_version,
251
- state='open'
266
+ state="open",
252
267
  )
253
- feedback['services_added'] += 1
268
+ feedback["services_added"] += 1
254
269
  logger.info(f"Added service {service_name} on port {port}")
255
270
  except Exception as e:
256
271
  logger.warning(f"Failed to add service {port}: {e}")
257
-
258
- if feedback['services_added'] > 0:
259
- feedback['notes_added'].append(
272
+
273
+ if feedback["services_added"] > 0:
274
+ feedback["notes_added"].append(
260
275
  f"Added {feedback['services_added']} services to host {host['ip_address']}"
261
276
  )
262
-
277
+
263
278
  return feedback
@@ -4,6 +4,7 @@ souleyez.ai.llm_factory - Factory for creating LLM providers
4
4
  This module provides a factory pattern for creating LLM providers
5
5
  based on user configuration.
6
6
  """
7
+
7
8
  import logging
8
9
  from typing import Optional
9
10
 
@@ -35,20 +36,24 @@ class LLMFactory:
35
36
 
36
37
  if provider_type is None:
37
38
  # Read from config
38
- provider_str = get('ai.provider', 'ollama')
39
+ provider_str = get("ai.provider", "ollama")
39
40
  try:
40
41
  provider_type = LLMProviderType(provider_str)
41
42
  except ValueError:
42
- logger.warning(f"Unknown provider '{provider_str}', defaulting to ollama")
43
+ logger.warning(
44
+ f"Unknown provider '{provider_str}', defaulting to ollama"
45
+ )
43
46
  provider_type = LLMProviderType.OLLAMA
44
47
 
45
48
  if provider_type == LLMProviderType.CLAUDE:
46
49
  from .claude_provider import ClaudeProvider
47
- model = get('ai.claude_model')
50
+
51
+ model = get("ai.claude_model")
48
52
  return ClaudeProvider(model=model)
49
53
  else:
50
54
  from .ollama_provider import OllamaProvider
51
- model = get('ai.ollama_model') or get('settings.ollama_model')
55
+
56
+ model = get("ai.ollama_model") or get("settings.ollama_model")
52
57
  return OllamaProvider(model=model)
53
58
 
54
59
  @staticmethod
@@ -103,33 +108,35 @@ class LLMFactory:
103
108
  # Check Ollama
104
109
  try:
105
110
  from .ollama_provider import OllamaProvider
111
+
106
112
  ollama = OllamaProvider()
107
- results['ollama'] = {
108
- 'available': ollama.is_available(),
109
- 'status': ollama.get_status(),
110
- 'configured': get('ai.provider', 'ollama') == 'ollama'
113
+ results["ollama"] = {
114
+ "available": ollama.is_available(),
115
+ "status": ollama.get_status(),
116
+ "configured": get("ai.provider", "ollama") == "ollama",
111
117
  }
112
118
  except Exception as e:
113
- results['ollama'] = {
114
- 'available': False,
115
- 'error': str(e),
116
- 'configured': get('ai.provider', 'ollama') == 'ollama'
119
+ results["ollama"] = {
120
+ "available": False,
121
+ "error": str(e),
122
+ "configured": get("ai.provider", "ollama") == "ollama",
117
123
  }
118
124
 
119
125
  # Check Claude
120
126
  try:
121
127
  from .claude_provider import ClaudeProvider
128
+
122
129
  claude = ClaudeProvider()
123
- results['claude'] = {
124
- 'available': claude.is_available(),
125
- 'status': claude.get_status(),
126
- 'configured': get('ai.provider', 'ollama') == 'claude'
130
+ results["claude"] = {
131
+ "available": claude.is_available(),
132
+ "status": claude.get_status(),
133
+ "configured": get("ai.provider", "ollama") == "claude",
127
134
  }
128
135
  except Exception as e:
129
- results['claude'] = {
130
- 'available': False,
131
- 'error': str(e),
132
- 'configured': get('ai.provider', 'ollama') == 'claude'
136
+ results["claude"] = {
137
+ "available": False,
138
+ "error": str(e),
139
+ "configured": get("ai.provider", "ollama") == "claude",
133
140
  }
134
141
 
135
142
  return results
@@ -4,6 +4,7 @@ souleyez.ai.llm_provider - Abstract LLM provider interface
4
4
  This module defines the abstract base class for LLM providers,
5
5
  enabling support for multiple backends (Ollama, Claude, etc.)
6
6
  """
7
+
7
8
  from abc import ABC, abstractmethod
8
9
  from enum import Enum
9
10
  from typing import Optional, Dict, Any
@@ -14,6 +15,7 @@ logger = logging.getLogger(__name__)
14
15
 
15
16
  class LLMProviderType(Enum):
16
17
  """Supported LLM provider types."""
18
+
17
19
  OLLAMA = "ollama"
18
20
  CLAUDE = "claude"
19
21
 
@@ -54,7 +56,7 @@ class LLMProvider(ABC):
54
56
  prompt: str,
55
57
  system_prompt: Optional[str] = None,
56
58
  max_tokens: int = 4096,
57
- temperature: float = 0.3
59
+ temperature: float = 0.3,
58
60
  ) -> Optional[str]:
59
61
  """
60
62
  Generate text from a prompt.
@@ -86,7 +88,7 @@ class LLMProvider(ABC):
86
88
  system_prompt: Optional[str] = None,
87
89
  max_tokens: int = 4096,
88
90
  temperature: float = 0.3,
89
- fallback: Optional[str] = None
91
+ fallback: Optional[str] = None,
90
92
  ) -> str:
91
93
  """
92
94
  Generate text with a fallback value if generation fails.
@@ -3,6 +3,7 @@ souleyez.ai.ollama_provider - Ollama LLM provider implementation
3
3
 
4
4
  Wraps the existing OllamaService to implement the LLMProvider interface.
5
5
  """
6
+
6
7
  import logging
7
8
  from typing import Optional, Dict, Any
8
9
 
@@ -20,11 +21,7 @@ class OllamaProvider(LLMProvider):
20
21
  No API key required, but Ollama must be installed and running.
21
22
  """
22
23
 
23
- def __init__(
24
- self,
25
- model: Optional[str] = None,
26
- endpoint: Optional[str] = None
27
- ):
24
+ def __init__(self, model: Optional[str] = None, endpoint: Optional[str] = None):
28
25
  """
29
26
  Initialize Ollama provider.
30
27
 
@@ -71,7 +68,7 @@ class OllamaProvider(LLMProvider):
71
68
  prompt: str,
72
69
  system_prompt: Optional[str] = None,
73
70
  max_tokens: int = 4096,
74
- temperature: float = 0.3
71
+ temperature: float = 0.3,
75
72
  ) -> Optional[str]:
76
73
  """
77
74
  Generate text using Ollama.
@@ -108,7 +105,7 @@ class OllamaProvider(LLMProvider):
108
105
  dict: Status including connection, models, availability
109
106
  """
110
107
  status = self._service.get_status()
111
- status['provider_type'] = self.provider_type.value
112
- status['provider'] = 'Ollama'
113
- status['provider_name'] = 'Ollama (Local)'
108
+ status["provider_type"] = self.provider_type.value
109
+ status["provider"] = "Ollama"
110
+ status["provider_name"] = "Ollama (Local)"
114
111
  return status