souleyez 2.43.29__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of souleyez might be problematic. Click here for more details.

Files changed (358) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9564 -2881
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1239 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2200 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +564 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +409 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +417 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +292 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/web_login_test_handler.py +327 -0
  126. souleyez/handlers/whois_handler.py +277 -0
  127. souleyez/handlers/wpscan_handler.py +554 -0
  128. souleyez/history.py +32 -16
  129. souleyez/importers/msf_importer.py +106 -75
  130. souleyez/importers/smart_importer.py +208 -147
  131. souleyez/integrations/siem/__init__.py +10 -10
  132. souleyez/integrations/siem/base.py +17 -18
  133. souleyez/integrations/siem/elastic.py +108 -122
  134. souleyez/integrations/siem/factory.py +207 -80
  135. souleyez/integrations/siem/googlesecops.py +146 -154
  136. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  137. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  138. souleyez/integrations/siem/sentinel.py +107 -109
  139. souleyez/integrations/siem/splunk.py +246 -212
  140. souleyez/integrations/siem/wazuh.py +65 -71
  141. souleyez/integrations/wazuh/__init__.py +5 -5
  142. souleyez/integrations/wazuh/client.py +70 -93
  143. souleyez/integrations/wazuh/config.py +85 -57
  144. souleyez/integrations/wazuh/host_mapper.py +28 -36
  145. souleyez/integrations/wazuh/sync.py +78 -68
  146. souleyez/intelligence/__init__.py +4 -5
  147. souleyez/intelligence/correlation_analyzer.py +309 -295
  148. souleyez/intelligence/exploit_knowledge.py +661 -623
  149. souleyez/intelligence/exploit_suggestions.py +159 -139
  150. souleyez/intelligence/gap_analyzer.py +132 -97
  151. souleyez/intelligence/gap_detector.py +251 -214
  152. souleyez/intelligence/sensitive_tables.py +266 -129
  153. souleyez/intelligence/service_parser.py +137 -123
  154. souleyez/intelligence/surface_analyzer.py +407 -268
  155. souleyez/intelligence/target_parser.py +159 -162
  156. souleyez/licensing/__init__.py +6 -6
  157. souleyez/licensing/validator.py +17 -19
  158. souleyez/log_config.py +79 -54
  159. souleyez/main.py +1505 -687
  160. souleyez/migrations/fix_job_counter.py +16 -14
  161. souleyez/parsers/bloodhound_parser.py +41 -39
  162. souleyez/parsers/crackmapexec_parser.py +178 -111
  163. souleyez/parsers/dalfox_parser.py +72 -77
  164. souleyez/parsers/dnsrecon_parser.py +103 -91
  165. souleyez/parsers/enum4linux_parser.py +183 -153
  166. souleyez/parsers/ffuf_parser.py +29 -25
  167. souleyez/parsers/gobuster_parser.py +301 -41
  168. souleyez/parsers/hashcat_parser.py +324 -79
  169. souleyez/parsers/http_fingerprint_parser.py +350 -103
  170. souleyez/parsers/hydra_parser.py +131 -111
  171. souleyez/parsers/impacket_parser.py +231 -178
  172. souleyez/parsers/john_parser.py +98 -86
  173. souleyez/parsers/katana_parser.py +316 -0
  174. souleyez/parsers/msf_parser.py +943 -498
  175. souleyez/parsers/nikto_parser.py +346 -65
  176. souleyez/parsers/nmap_parser.py +262 -174
  177. souleyez/parsers/nuclei_parser.py +40 -44
  178. souleyez/parsers/responder_parser.py +26 -26
  179. souleyez/parsers/searchsploit_parser.py +74 -74
  180. souleyez/parsers/service_explorer_parser.py +279 -0
  181. souleyez/parsers/smbmap_parser.py +180 -124
  182. souleyez/parsers/sqlmap_parser.py +434 -308
  183. souleyez/parsers/theharvester_parser.py +75 -57
  184. souleyez/parsers/whois_parser.py +135 -94
  185. souleyez/parsers/wpscan_parser.py +278 -190
  186. souleyez/plugins/afp.py +44 -36
  187. souleyez/plugins/afp_brute.py +114 -46
  188. souleyez/plugins/ard.py +48 -37
  189. souleyez/plugins/bloodhound.py +95 -61
  190. souleyez/plugins/certipy.py +303 -0
  191. souleyez/plugins/crackmapexec.py +186 -85
  192. souleyez/plugins/dalfox.py +120 -59
  193. souleyez/plugins/dns_hijack.py +146 -41
  194. souleyez/plugins/dnsrecon.py +97 -61
  195. souleyez/plugins/enum4linux.py +91 -66
  196. souleyez/plugins/evil_winrm.py +291 -0
  197. souleyez/plugins/ffuf.py +166 -90
  198. souleyez/plugins/firmware_extract.py +133 -29
  199. souleyez/plugins/gobuster.py +387 -190
  200. souleyez/plugins/gpp_extract.py +393 -0
  201. souleyez/plugins/hashcat.py +100 -73
  202. souleyez/plugins/http_fingerprint.py +913 -267
  203. souleyez/plugins/hydra.py +566 -200
  204. souleyez/plugins/impacket_getnpusers.py +117 -69
  205. souleyez/plugins/impacket_psexec.py +84 -64
  206. souleyez/plugins/impacket_secretsdump.py +103 -69
  207. souleyez/plugins/impacket_smbclient.py +89 -75
  208. souleyez/plugins/john.py +86 -69
  209. souleyez/plugins/katana.py +313 -0
  210. souleyez/plugins/kerbrute.py +237 -0
  211. souleyez/plugins/lfi_extract.py +541 -0
  212. souleyez/plugins/macos_ssh.py +117 -48
  213. souleyez/plugins/mdns.py +35 -30
  214. souleyez/plugins/msf_auxiliary.py +253 -130
  215. souleyez/plugins/msf_exploit.py +239 -161
  216. souleyez/plugins/nikto.py +134 -78
  217. souleyez/plugins/nmap.py +275 -91
  218. souleyez/plugins/nuclei.py +180 -89
  219. souleyez/plugins/nxc.py +285 -0
  220. souleyez/plugins/plugin_base.py +35 -36
  221. souleyez/plugins/plugin_template.py +13 -5
  222. souleyez/plugins/rdp_sec_check.py +130 -0
  223. souleyez/plugins/responder.py +112 -71
  224. souleyez/plugins/router_http_brute.py +76 -65
  225. souleyez/plugins/router_ssh_brute.py +118 -41
  226. souleyez/plugins/router_telnet_brute.py +124 -42
  227. souleyez/plugins/routersploit.py +91 -59
  228. souleyez/plugins/routersploit_exploit.py +77 -55
  229. souleyez/plugins/searchsploit.py +91 -77
  230. souleyez/plugins/service_explorer.py +1160 -0
  231. souleyez/plugins/smbmap.py +122 -72
  232. souleyez/plugins/smbpasswd.py +215 -0
  233. souleyez/plugins/sqlmap.py +301 -113
  234. souleyez/plugins/theharvester.py +127 -75
  235. souleyez/plugins/tr069.py +79 -57
  236. souleyez/plugins/upnp.py +65 -47
  237. souleyez/plugins/upnp_abuse.py +73 -55
  238. souleyez/plugins/vnc_access.py +129 -42
  239. souleyez/plugins/vnc_brute.py +109 -38
  240. souleyez/plugins/web_login_test.py +417 -0
  241. souleyez/plugins/whois.py +77 -58
  242. souleyez/plugins/wpscan.py +219 -69
  243. souleyez/reporting/__init__.py +2 -1
  244. souleyez/reporting/attack_chain.py +411 -346
  245. souleyez/reporting/charts.py +436 -501
  246. souleyez/reporting/compliance_mappings.py +334 -201
  247. souleyez/reporting/detection_report.py +126 -125
  248. souleyez/reporting/formatters.py +828 -591
  249. souleyez/reporting/generator.py +386 -302
  250. souleyez/reporting/metrics.py +72 -75
  251. souleyez/scanner.py +35 -29
  252. souleyez/security/__init__.py +37 -11
  253. souleyez/security/scope_validator.py +175 -106
  254. souleyez/security/validation.py +237 -149
  255. souleyez/security.py +22 -6
  256. souleyez/storage/credentials.py +247 -186
  257. souleyez/storage/crypto.py +296 -129
  258. souleyez/storage/database.py +73 -50
  259. souleyez/storage/db.py +58 -36
  260. souleyez/storage/deliverable_evidence.py +177 -128
  261. souleyez/storage/deliverable_exporter.py +282 -246
  262. souleyez/storage/deliverable_templates.py +134 -116
  263. souleyez/storage/deliverables.py +135 -130
  264. souleyez/storage/engagements.py +109 -56
  265. souleyez/storage/evidence.py +181 -152
  266. souleyez/storage/execution_log.py +31 -17
  267. souleyez/storage/exploit_attempts.py +93 -57
  268. souleyez/storage/exploits.py +67 -36
  269. souleyez/storage/findings.py +48 -61
  270. souleyez/storage/hosts.py +176 -144
  271. souleyez/storage/migrate_to_engagements.py +43 -19
  272. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  273. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  274. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  275. souleyez/storage/migrations/_005_screenshots.py +13 -5
  276. souleyez/storage/migrations/_006_deliverables.py +13 -5
  277. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  278. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  279. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  280. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  281. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  282. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  283. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  284. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  285. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  286. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  287. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  288. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  289. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  290. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  291. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  292. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  293. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  294. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  295. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  296. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  297. souleyez/storage/migrations/__init__.py +26 -26
  298. souleyez/storage/migrations/migration_manager.py +19 -19
  299. souleyez/storage/msf_sessions.py +100 -65
  300. souleyez/storage/osint.py +17 -24
  301. souleyez/storage/recommendation_engine.py +269 -235
  302. souleyez/storage/screenshots.py +33 -32
  303. souleyez/storage/smb_shares.py +136 -92
  304. souleyez/storage/sqlmap_data.py +183 -128
  305. souleyez/storage/team_collaboration.py +135 -141
  306. souleyez/storage/timeline_tracker.py +122 -94
  307. souleyez/storage/wazuh_vulns.py +64 -66
  308. souleyez/storage/web_paths.py +33 -37
  309. souleyez/testing/credential_tester.py +221 -205
  310. souleyez/ui/__init__.py +1 -1
  311. souleyez/ui/ai_quotes.py +12 -12
  312. souleyez/ui/attack_surface.py +2439 -1516
  313. souleyez/ui/chain_rules_view.py +914 -382
  314. souleyez/ui/correlation_view.py +312 -230
  315. souleyez/ui/dashboard.py +2382 -1130
  316. souleyez/ui/deliverables_view.py +148 -62
  317. souleyez/ui/design_system.py +13 -13
  318. souleyez/ui/errors.py +49 -49
  319. souleyez/ui/evidence_linking_view.py +284 -179
  320. souleyez/ui/evidence_vault.py +393 -285
  321. souleyez/ui/exploit_suggestions_view.py +555 -349
  322. souleyez/ui/export_view.py +100 -66
  323. souleyez/ui/gap_analysis_view.py +315 -171
  324. souleyez/ui/help_system.py +105 -97
  325. souleyez/ui/intelligence_view.py +436 -293
  326. souleyez/ui/interactive.py +23034 -10679
  327. souleyez/ui/interactive_selector.py +75 -68
  328. souleyez/ui/log_formatter.py +47 -39
  329. souleyez/ui/menu_components.py +22 -13
  330. souleyez/ui/msf_auxiliary_menu.py +184 -133
  331. souleyez/ui/pending_chains_view.py +336 -172
  332. souleyez/ui/progress_indicators.py +5 -3
  333. souleyez/ui/recommendations_view.py +195 -137
  334. souleyez/ui/rule_builder.py +343 -225
  335. souleyez/ui/setup_wizard.py +678 -284
  336. souleyez/ui/shortcuts.py +217 -165
  337. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  338. souleyez/ui/splunk_vulns_view.py +139 -86
  339. souleyez/ui/team_dashboard.py +498 -335
  340. souleyez/ui/template_selector.py +196 -105
  341. souleyez/ui/terminal.py +6 -6
  342. souleyez/ui/timeline_view.py +198 -127
  343. souleyez/ui/tool_setup.py +264 -164
  344. souleyez/ui/tutorial.py +202 -72
  345. souleyez/ui/tutorial_state.py +40 -40
  346. souleyez/ui/wazuh_vulns_view.py +235 -141
  347. souleyez/ui/wordlist_browser.py +260 -107
  348. souleyez/ui.py +464 -312
  349. souleyez/utils/tool_checker.py +427 -367
  350. souleyez/utils.py +33 -29
  351. souleyez/wordlists.py +134 -167
  352. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
  353. souleyez-3.0.0.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
@@ -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