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
@@ -1,34 +1,35 @@
1
1
  """
2
2
  Evidence linking for deliverables.
3
3
  """
4
+
4
5
  from typing import List, Dict, Optional
5
6
  from .database import get_db
6
7
 
7
8
 
8
9
  class EvidenceManager:
9
10
  """Manage evidence links for deliverables."""
10
-
11
+
11
12
  def __init__(self):
12
13
  self.db = get_db()
13
-
14
+
14
15
  def link_evidence(
15
16
  self,
16
17
  deliverable_id: int,
17
18
  evidence_type: str,
18
19
  evidence_id: int,
19
20
  linked_by: str = None,
20
- notes: str = None
21
+ notes: str = None,
21
22
  ) -> int:
22
23
  """
23
24
  Link evidence to deliverable.
24
-
25
+
25
26
  Args:
26
27
  deliverable_id: Target deliverable
27
28
  evidence_type: 'finding', 'screenshot', 'credential', 'job'
28
29
  evidence_id: ID of the evidence item
29
30
  linked_by: Username who created the link
30
31
  notes: Optional notes about the link
31
-
32
+
32
33
  Returns:
33
34
  Link ID
34
35
  """
@@ -36,82 +37,80 @@ class EvidenceManager:
36
37
  existing = self.db.execute_one(
37
38
  """SELECT id FROM deliverable_evidence
38
39
  WHERE deliverable_id = ? AND evidence_type = ? AND evidence_id = ?""",
39
- (deliverable_id, evidence_type, evidence_id)
40
+ (deliverable_id, evidence_type, evidence_id),
40
41
  )
41
-
42
+
42
43
  if existing:
43
- return existing['id'] # Already linked
44
-
45
- return self.db.insert('deliverable_evidence', {
46
- 'deliverable_id': deliverable_id,
47
- 'evidence_type': evidence_type,
48
- 'evidence_id': evidence_id,
49
- 'linked_by': linked_by,
50
- 'notes': notes
51
- })
52
-
44
+ return existing["id"] # Already linked
45
+
46
+ return self.db.insert(
47
+ "deliverable_evidence",
48
+ {
49
+ "deliverable_id": deliverable_id,
50
+ "evidence_type": evidence_type,
51
+ "evidence_id": evidence_id,
52
+ "linked_by": linked_by,
53
+ "notes": notes,
54
+ },
55
+ )
56
+
53
57
  def get_evidence(self, deliverable_id: int) -> Dict:
54
58
  """
55
59
  Get all evidence for deliverable, grouped by type.
56
-
60
+
57
61
  Returns:
58
62
  Dict with keys: findings, screenshots, credentials, jobs
59
63
  """
60
64
  links = self.db.execute(
61
65
  "SELECT * FROM deliverable_evidence WHERE deliverable_id = ? ORDER BY linked_at DESC",
62
- (deliverable_id,)
66
+ (deliverable_id,),
63
67
  )
64
-
65
- evidence = {
66
- 'findings': [],
67
- 'screenshots': [],
68
- 'credentials': [],
69
- 'jobs': []
70
- }
71
-
68
+
69
+ evidence = {"findings": [], "screenshots": [], "credentials": [], "jobs": []}
70
+
72
71
  for link in links:
73
- etype = link['evidence_type']
74
- eid = link['evidence_id']
75
-
72
+ etype = link["evidence_type"]
73
+ eid = link["evidence_id"]
74
+
76
75
  # Fetch actual evidence data
77
- if etype == 'finding':
76
+ if etype == "finding":
78
77
  finding = self._get_finding(eid)
79
78
  if finding:
80
- finding['_link_notes'] = link['notes']
81
- finding['_linked_at'] = link['linked_at']
82
- finding['_link_id'] = link['id']
83
- evidence['findings'].append(finding)
84
-
85
- elif etype == 'screenshot':
79
+ finding["_link_notes"] = link["notes"]
80
+ finding["_linked_at"] = link["linked_at"]
81
+ finding["_link_id"] = link["id"]
82
+ evidence["findings"].append(finding)
83
+
84
+ elif etype == "screenshot":
86
85
  screenshot = self._get_screenshot(eid)
87
86
  if screenshot:
88
- screenshot['_link_notes'] = link['notes']
89
- screenshot['_linked_at'] = link['linked_at']
90
- screenshot['_link_id'] = link['id']
91
- evidence['screenshots'].append(screenshot)
92
-
93
- elif etype == 'credential':
87
+ screenshot["_link_notes"] = link["notes"]
88
+ screenshot["_linked_at"] = link["linked_at"]
89
+ screenshot["_link_id"] = link["id"]
90
+ evidence["screenshots"].append(screenshot)
91
+
92
+ elif etype == "credential":
94
93
  credential = self._get_credential(eid)
95
94
  if credential:
96
- credential['_link_notes'] = link['notes']
97
- credential['_linked_at'] = link['linked_at']
98
- credential['_link_id'] = link['id']
99
- evidence['credentials'].append(credential)
100
-
101
- elif etype == 'job':
95
+ credential["_link_notes"] = link["notes"]
96
+ credential["_linked_at"] = link["linked_at"]
97
+ credential["_link_id"] = link["id"]
98
+ evidence["credentials"].append(credential)
99
+
100
+ elif etype == "job":
102
101
  job = self._get_job(eid)
103
102
  if job:
104
- job['_link_notes'] = link['notes']
105
- job['_linked_at'] = link['linked_at']
106
- job['_link_id'] = link['id']
107
- evidence['jobs'].append(job)
108
-
103
+ job["_link_notes"] = link["notes"]
104
+ job["_linked_at"] = link["linked_at"]
105
+ job["_link_id"] = link["id"]
106
+ evidence["jobs"].append(job)
107
+
109
108
  return evidence
110
-
109
+
111
110
  def suggest_evidence(self, deliverable_id: int, engagement_id: int = None) -> Dict:
112
111
  """
113
112
  AI-suggest related evidence based on deliverable keywords.
114
-
113
+
115
114
  Args:
116
115
  deliverable_id: Target deliverable
117
116
  engagement_id: Optional engagement filter
@@ -119,143 +118,184 @@ class EvidenceManager:
119
118
  Dict with suggested evidence items and confidence scores
120
119
  """
121
120
  from .deliverables import DeliverableManager
121
+
122
122
  dm = DeliverableManager()
123
123
  deliverable = dm.get_deliverable(deliverable_id)
124
124
  if not deliverable:
125
125
  # Return empty suggestions and helpful error
126
126
  return {
127
- 'findings': [],
128
- 'screenshots': [],
129
- 'credentials': [],
130
- 'jobs': [],
131
- '_error': 'Deliverable not found or invalid ID.'
127
+ "findings": [],
128
+ "screenshots": [],
129
+ "credentials": [],
130
+ "jobs": [],
131
+ "_error": "Deliverable not found or invalid ID.",
132
132
  }
133
133
  if not engagement_id:
134
- engagement_id = deliverable.get('engagement_id')
134
+ engagement_id = deliverable.get("engagement_id")
135
135
  # Extract keywords from title and description safely
136
- title = (deliverable.get('title') or '').lower()
137
- description = (deliverable.get('description') or '').lower()
136
+ title = (deliverable.get("title") or "").lower()
137
+ description = (deliverable.get("description") or "").lower()
138
138
  combined_text = f"{title} {description}"
139
139
  keywords = self._extract_keywords(combined_text)
140
- suggestions = {
141
- 'findings': [],
142
- 'screenshots': [],
143
- 'credentials': [],
144
- 'jobs': []
145
- }
146
-
140
+ suggestions = {"findings": [], "screenshots": [], "credentials": [], "jobs": []}
141
+
147
142
  # Find related findings
148
143
  for keyword in keywords[:5]: # Top 5 keywords
149
144
  findings = self.db.execute(
150
145
  """SELECT * FROM findings
151
146
  WHERE engagement_id = ? AND LOWER(title) LIKE ?
152
147
  LIMIT 10""",
153
- (engagement_id, f'%{keyword}%')
148
+ (engagement_id, f"%{keyword}%"),
154
149
  )
155
150
  for f in findings:
156
151
  # Avoid duplicates
157
- if not any(existing['id'] == f['id'] for existing in suggestions['findings']):
158
- f['_match_keyword'] = keyword
159
- f['_confidence'] = self._calculate_confidence(combined_text, f.get('title', ''))
160
- suggestions['findings'].append(f)
161
-
152
+ if not any(
153
+ existing["id"] == f["id"] for existing in suggestions["findings"]
154
+ ):
155
+ f["_match_keyword"] = keyword
156
+ f["_confidence"] = self._calculate_confidence(
157
+ combined_text, f.get("title", "")
158
+ )
159
+ suggestions["findings"].append(f)
160
+
162
161
  # Find related credentials (if deliverable mentions credentials/users/passwords)
163
- if any(kw in combined_text for kw in ['credential', 'user', 'password', 'account', 'login', 'auth']):
162
+ if any(
163
+ kw in combined_text
164
+ for kw in ["credential", "user", "password", "account", "login", "auth"]
165
+ ):
164
166
  credentials = self.db.execute(
165
167
  "SELECT * FROM credentials WHERE engagement_id = ? LIMIT 20",
166
- (engagement_id,)
168
+ (engagement_id,),
167
169
  )
168
170
  for c in credentials:
169
- c['_confidence'] = 70 # Medium confidence for keyword match
170
- suggestions['credentials'].extend(credentials)
171
-
171
+ c["_confidence"] = 70 # Medium confidence for keyword match
172
+ suggestions["credentials"].extend(credentials)
173
+
172
174
  # Find related screenshots
173
175
  screenshots = self.db.execute(
174
176
  "SELECT * FROM screenshots WHERE engagement_id = ? ORDER BY created_at DESC LIMIT 10",
175
- (engagement_id,)
177
+ (engagement_id,),
176
178
  )
177
179
  for s in screenshots:
178
- s['_confidence'] = 50 # Lower confidence for screenshots
179
- suggestions['screenshots'] = screenshots
180
-
180
+ s["_confidence"] = 50 # Lower confidence for screenshots
181
+ suggestions["screenshots"] = screenshots
182
+
181
183
  # Sort findings by confidence
182
- suggestions['findings'].sort(key=lambda x: x.get('_confidence', 0), reverse=True)
183
-
184
+ suggestions["findings"].sort(
185
+ key=lambda x: x.get("_confidence", 0), reverse=True
186
+ )
187
+
184
188
  # Limit results
185
- suggestions['findings'] = suggestions['findings'][:10]
186
- suggestions['credentials'] = suggestions['credentials'][:10]
187
-
189
+ suggestions["findings"] = suggestions["findings"][:10]
190
+ suggestions["credentials"] = suggestions["credentials"][:10]
191
+
188
192
  return suggestions
189
-
190
- def unlink_evidence(self, deliverable_id: int, evidence_type: str, evidence_id: int) -> bool:
193
+
194
+ def unlink_evidence(
195
+ self, deliverable_id: int, evidence_type: str, evidence_id: int
196
+ ) -> bool:
191
197
  """Remove evidence link."""
192
198
  self.db.execute(
193
199
  """DELETE FROM deliverable_evidence
194
200
  WHERE deliverable_id = ? AND evidence_type = ? AND evidence_id = ?""",
195
- (deliverable_id, evidence_type, evidence_id)
201
+ (deliverable_id, evidence_type, evidence_id),
196
202
  )
197
203
  return True
198
-
204
+
199
205
  def get_evidence_count(self, deliverable_id: int) -> int:
200
206
  """Get count of linked evidence items."""
201
207
  result = self.db.execute_one(
202
208
  "SELECT COUNT(*) as count FROM deliverable_evidence WHERE deliverable_id = ?",
203
- (deliverable_id,)
209
+ (deliverable_id,),
204
210
  )
205
- return result['count'] if result else 0
206
-
207
- def get_deliverables_for_evidence(self, evidence_type: str, evidence_id: int) -> List[Dict]:
211
+ return result["count"] if result else 0
212
+
213
+ def get_deliverables_for_evidence(
214
+ self, evidence_type: str, evidence_id: int
215
+ ) -> List[Dict]:
208
216
  """Get all deliverables linked to this evidence item."""
209
217
  links = self.db.execute(
210
218
  """SELECT * FROM deliverable_evidence
211
219
  WHERE evidence_type = ? AND evidence_id = ?""",
212
- (evidence_type, evidence_id)
220
+ (evidence_type, evidence_id),
213
221
  )
214
-
222
+
215
223
  deliverables = []
216
224
  if links:
217
225
  from .deliverables import DeliverableManager
226
+
218
227
  dm = DeliverableManager()
219
-
228
+
220
229
  for link in links:
221
- d = dm.get_deliverable(link['deliverable_id'])
230
+ d = dm.get_deliverable(link["deliverable_id"])
222
231
  if d:
223
- d['_link_notes'] = link['notes']
224
- d['_linked_at'] = link['linked_at']
232
+ d["_link_notes"] = link["notes"]
233
+ d["_linked_at"] = link["linked_at"]
225
234
  deliverables.append(d)
226
-
235
+
227
236
  return deliverables
228
-
237
+
229
238
  def _get_finding(self, finding_id: int) -> Optional[Dict]:
230
239
  """Fetch finding by ID."""
231
240
  return self.db.execute_one("SELECT * FROM findings WHERE id = ?", (finding_id,))
232
-
241
+
233
242
  def _get_screenshot(self, screenshot_id: int) -> Optional[Dict]:
234
243
  """Fetch screenshot by ID."""
235
- return self.db.execute_one("SELECT * FROM screenshots WHERE id = ?", (screenshot_id,))
236
-
244
+ return self.db.execute_one(
245
+ "SELECT * FROM screenshots WHERE id = ?", (screenshot_id,)
246
+ )
247
+
237
248
  def _get_credential(self, credential_id: int) -> Optional[Dict]:
238
249
  """Fetch credential by ID."""
239
- return self.db.execute_one("SELECT * FROM credentials WHERE id = ?", (credential_id,))
240
-
250
+ return self.db.execute_one(
251
+ "SELECT * FROM credentials WHERE id = ?", (credential_id,)
252
+ )
253
+
241
254
  def _get_job(self, job_id: int) -> Optional[Dict]:
242
255
  """Fetch job by ID."""
243
256
  from souleyez.engine.background import list_jobs
257
+
244
258
  jobs = list_jobs(limit=1000)
245
259
  for job in jobs:
246
- if job.get('id') == job_id:
260
+ if job.get("id") == job_id:
247
261
  return job
248
262
  return None
249
-
263
+
250
264
  def _extract_keywords(self, text: str) -> List[str]:
251
265
  """Extract meaningful keywords from text."""
252
266
  stopwords = {
253
- 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
254
- 'of', 'with', 'from', 'by', 'as', 'is', 'are', 'was', 'were', 'be',
255
- 'this', 'that', 'which', 'what', 'when', 'where', 'who', 'how'
267
+ "the",
268
+ "a",
269
+ "an",
270
+ "and",
271
+ "or",
272
+ "but",
273
+ "in",
274
+ "on",
275
+ "at",
276
+ "to",
277
+ "for",
278
+ "of",
279
+ "with",
280
+ "from",
281
+ "by",
282
+ "as",
283
+ "is",
284
+ "are",
285
+ "was",
286
+ "were",
287
+ "be",
288
+ "this",
289
+ "that",
290
+ "which",
291
+ "what",
292
+ "when",
293
+ "where",
294
+ "who",
295
+ "how",
256
296
  }
257
297
  # Clean and split text
258
- words = text.lower().replace('-', ' ').replace('_', ' ').split()
298
+ words = text.lower().replace("-", " ").replace("_", " ").split()
259
299
  # Filter stopwords and short words
260
300
  keywords = [w for w in words if len(w) > 3 and w not in stopwords]
261
301
  # Remove duplicates while preserving order
@@ -266,34 +306,43 @@ class EvidenceManager:
266
306
  seen.add(kw)
267
307
  unique_keywords.append(kw)
268
308
  return unique_keywords
269
-
309
+
270
310
  def _calculate_confidence(self, deliverable_text: str, finding_title: str) -> int:
271
311
  """
272
312
  Calculate match confidence (0-100).
273
-
313
+
274
314
  Higher score means better match between deliverable and finding.
275
315
  """
276
316
  if not finding_title:
277
317
  return 30 # Low confidence
278
-
318
+
279
319
  d_words = set(deliverable_text.lower().split())
280
320
  f_words = set(finding_title.lower().split())
281
-
321
+
282
322
  common = d_words & f_words
283
323
  if not common:
284
324
  return 30 # Low confidence
285
-
325
+
286
326
  # Calculate Jaccard similarity
287
327
  union = d_words | f_words
288
328
  ratio = len(common) / len(union) if union else 0
289
-
329
+
290
330
  # Boost score if important security terms match
291
- important_terms = {'sql', 'xss', 'injection', 'authentication', 'authorization',
292
- 'encryption', 'credential', 'vulnerability', 'exploit'}
331
+ important_terms = {
332
+ "sql",
333
+ "xss",
334
+ "injection",
335
+ "authentication",
336
+ "authorization",
337
+ "encryption",
338
+ "credential",
339
+ "vulnerability",
340
+ "exploit",
341
+ }
293
342
  important_matches = common & important_terms
294
343
  boost = len(important_matches) * 10
295
-
344
+
296
345
  base_score = int(ratio * 100)
297
346
  final_score = min(100, base_score + boost)
298
-
347
+
299
348
  return final_score