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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9592 -2879
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1238 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2198 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +563 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +408 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +371 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +288 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/whois_handler.py +277 -0
  126. souleyez/handlers/wpscan_handler.py +554 -0
  127. souleyez/history.py +32 -16
  128. souleyez/importers/msf_importer.py +106 -75
  129. souleyez/importers/smart_importer.py +208 -147
  130. souleyez/integrations/siem/__init__.py +10 -10
  131. souleyez/integrations/siem/base.py +17 -18
  132. souleyez/integrations/siem/elastic.py +108 -122
  133. souleyez/integrations/siem/factory.py +207 -80
  134. souleyez/integrations/siem/googlesecops.py +146 -154
  135. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  136. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  137. souleyez/integrations/siem/sentinel.py +107 -109
  138. souleyez/integrations/siem/splunk.py +246 -212
  139. souleyez/integrations/siem/wazuh.py +65 -71
  140. souleyez/integrations/wazuh/__init__.py +5 -5
  141. souleyez/integrations/wazuh/client.py +70 -93
  142. souleyez/integrations/wazuh/config.py +85 -57
  143. souleyez/integrations/wazuh/host_mapper.py +28 -36
  144. souleyez/integrations/wazuh/sync.py +78 -68
  145. souleyez/intelligence/__init__.py +4 -5
  146. souleyez/intelligence/correlation_analyzer.py +309 -295
  147. souleyez/intelligence/exploit_knowledge.py +661 -623
  148. souleyez/intelligence/exploit_suggestions.py +159 -139
  149. souleyez/intelligence/gap_analyzer.py +132 -97
  150. souleyez/intelligence/gap_detector.py +251 -214
  151. souleyez/intelligence/sensitive_tables.py +266 -129
  152. souleyez/intelligence/service_parser.py +137 -123
  153. souleyez/intelligence/surface_analyzer.py +407 -268
  154. souleyez/intelligence/target_parser.py +159 -162
  155. souleyez/licensing/__init__.py +6 -6
  156. souleyez/licensing/validator.py +17 -19
  157. souleyez/log_config.py +79 -54
  158. souleyez/main.py +1505 -687
  159. souleyez/migrations/fix_job_counter.py +16 -14
  160. souleyez/parsers/bloodhound_parser.py +41 -39
  161. souleyez/parsers/crackmapexec_parser.py +178 -111
  162. souleyez/parsers/dalfox_parser.py +72 -77
  163. souleyez/parsers/dnsrecon_parser.py +103 -91
  164. souleyez/parsers/enum4linux_parser.py +183 -153
  165. souleyez/parsers/ffuf_parser.py +29 -25
  166. souleyez/parsers/gobuster_parser.py +301 -41
  167. souleyez/parsers/hashcat_parser.py +324 -79
  168. souleyez/parsers/http_fingerprint_parser.py +350 -103
  169. souleyez/parsers/hydra_parser.py +131 -111
  170. souleyez/parsers/impacket_parser.py +231 -178
  171. souleyez/parsers/john_parser.py +98 -86
  172. souleyez/parsers/katana_parser.py +316 -0
  173. souleyez/parsers/msf_parser.py +943 -498
  174. souleyez/parsers/nikto_parser.py +346 -65
  175. souleyez/parsers/nmap_parser.py +262 -174
  176. souleyez/parsers/nuclei_parser.py +40 -44
  177. souleyez/parsers/responder_parser.py +26 -26
  178. souleyez/parsers/searchsploit_parser.py +74 -74
  179. souleyez/parsers/service_explorer_parser.py +279 -0
  180. souleyez/parsers/smbmap_parser.py +180 -124
  181. souleyez/parsers/sqlmap_parser.py +434 -308
  182. souleyez/parsers/theharvester_parser.py +75 -57
  183. souleyez/parsers/whois_parser.py +135 -94
  184. souleyez/parsers/wpscan_parser.py +278 -190
  185. souleyez/plugins/afp.py +44 -36
  186. souleyez/plugins/afp_brute.py +114 -46
  187. souleyez/plugins/ard.py +48 -37
  188. souleyez/plugins/bloodhound.py +95 -61
  189. souleyez/plugins/certipy.py +303 -0
  190. souleyez/plugins/crackmapexec.py +186 -85
  191. souleyez/plugins/dalfox.py +120 -59
  192. souleyez/plugins/dns_hijack.py +146 -41
  193. souleyez/plugins/dnsrecon.py +97 -61
  194. souleyez/plugins/enum4linux.py +91 -66
  195. souleyez/plugins/evil_winrm.py +291 -0
  196. souleyez/plugins/ffuf.py +166 -90
  197. souleyez/plugins/firmware_extract.py +133 -29
  198. souleyez/plugins/gobuster.py +387 -190
  199. souleyez/plugins/gpp_extract.py +393 -0
  200. souleyez/plugins/hashcat.py +100 -73
  201. souleyez/plugins/http_fingerprint.py +854 -267
  202. souleyez/plugins/hydra.py +566 -200
  203. souleyez/plugins/impacket_getnpusers.py +117 -69
  204. souleyez/plugins/impacket_psexec.py +84 -64
  205. souleyez/plugins/impacket_secretsdump.py +103 -69
  206. souleyez/plugins/impacket_smbclient.py +89 -75
  207. souleyez/plugins/john.py +86 -69
  208. souleyez/plugins/katana.py +313 -0
  209. souleyez/plugins/kerbrute.py +237 -0
  210. souleyez/plugins/lfi_extract.py +541 -0
  211. souleyez/plugins/macos_ssh.py +117 -48
  212. souleyez/plugins/mdns.py +35 -30
  213. souleyez/plugins/msf_auxiliary.py +253 -130
  214. souleyez/plugins/msf_exploit.py +239 -161
  215. souleyez/plugins/nikto.py +134 -78
  216. souleyez/plugins/nmap.py +275 -91
  217. souleyez/plugins/nuclei.py +180 -89
  218. souleyez/plugins/nxc.py +285 -0
  219. souleyez/plugins/plugin_base.py +35 -36
  220. souleyez/plugins/plugin_template.py +13 -5
  221. souleyez/plugins/rdp_sec_check.py +130 -0
  222. souleyez/plugins/responder.py +112 -71
  223. souleyez/plugins/router_http_brute.py +76 -65
  224. souleyez/plugins/router_ssh_brute.py +118 -41
  225. souleyez/plugins/router_telnet_brute.py +124 -42
  226. souleyez/plugins/routersploit.py +91 -59
  227. souleyez/plugins/routersploit_exploit.py +77 -55
  228. souleyez/plugins/searchsploit.py +91 -77
  229. souleyez/plugins/service_explorer.py +1160 -0
  230. souleyez/plugins/smbmap.py +122 -72
  231. souleyez/plugins/smbpasswd.py +215 -0
  232. souleyez/plugins/sqlmap.py +301 -113
  233. souleyez/plugins/theharvester.py +127 -75
  234. souleyez/plugins/tr069.py +79 -57
  235. souleyez/plugins/upnp.py +65 -47
  236. souleyez/plugins/upnp_abuse.py +73 -55
  237. souleyez/plugins/vnc_access.py +129 -42
  238. souleyez/plugins/vnc_brute.py +109 -38
  239. souleyez/plugins/whois.py +77 -58
  240. souleyez/plugins/wpscan.py +173 -69
  241. souleyez/reporting/__init__.py +2 -1
  242. souleyez/reporting/attack_chain.py +411 -346
  243. souleyez/reporting/charts.py +436 -501
  244. souleyez/reporting/compliance_mappings.py +334 -201
  245. souleyez/reporting/detection_report.py +126 -125
  246. souleyez/reporting/formatters.py +828 -591
  247. souleyez/reporting/generator.py +386 -302
  248. souleyez/reporting/metrics.py +72 -75
  249. souleyez/scanner.py +35 -29
  250. souleyez/security/__init__.py +37 -11
  251. souleyez/security/scope_validator.py +175 -106
  252. souleyez/security/validation.py +223 -149
  253. souleyez/security.py +22 -6
  254. souleyez/storage/credentials.py +247 -186
  255. souleyez/storage/crypto.py +296 -129
  256. souleyez/storage/database.py +73 -50
  257. souleyez/storage/db.py +58 -36
  258. souleyez/storage/deliverable_evidence.py +177 -128
  259. souleyez/storage/deliverable_exporter.py +282 -246
  260. souleyez/storage/deliverable_templates.py +134 -116
  261. souleyez/storage/deliverables.py +135 -130
  262. souleyez/storage/engagements.py +109 -56
  263. souleyez/storage/evidence.py +181 -152
  264. souleyez/storage/execution_log.py +31 -17
  265. souleyez/storage/exploit_attempts.py +93 -57
  266. souleyez/storage/exploits.py +67 -36
  267. souleyez/storage/findings.py +48 -61
  268. souleyez/storage/hosts.py +176 -144
  269. souleyez/storage/migrate_to_engagements.py +43 -19
  270. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  271. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  272. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  273. souleyez/storage/migrations/_005_screenshots.py +13 -5
  274. souleyez/storage/migrations/_006_deliverables.py +13 -5
  275. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  276. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  277. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  278. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  279. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  280. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  281. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  282. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  283. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  284. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  285. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  286. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  287. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  288. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  289. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  290. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  291. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  292. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  293. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  294. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  295. souleyez/storage/migrations/__init__.py +26 -26
  296. souleyez/storage/migrations/migration_manager.py +19 -19
  297. souleyez/storage/msf_sessions.py +100 -65
  298. souleyez/storage/osint.py +17 -24
  299. souleyez/storage/recommendation_engine.py +269 -235
  300. souleyez/storage/screenshots.py +33 -32
  301. souleyez/storage/smb_shares.py +136 -92
  302. souleyez/storage/sqlmap_data.py +183 -128
  303. souleyez/storage/team_collaboration.py +135 -141
  304. souleyez/storage/timeline_tracker.py +122 -94
  305. souleyez/storage/wazuh_vulns.py +64 -66
  306. souleyez/storage/web_paths.py +33 -37
  307. souleyez/testing/credential_tester.py +221 -205
  308. souleyez/ui/__init__.py +1 -1
  309. souleyez/ui/ai_quotes.py +12 -12
  310. souleyez/ui/attack_surface.py +2439 -1516
  311. souleyez/ui/chain_rules_view.py +914 -382
  312. souleyez/ui/correlation_view.py +312 -230
  313. souleyez/ui/dashboard.py +2382 -1130
  314. souleyez/ui/deliverables_view.py +148 -62
  315. souleyez/ui/design_system.py +13 -13
  316. souleyez/ui/errors.py +49 -49
  317. souleyez/ui/evidence_linking_view.py +284 -179
  318. souleyez/ui/evidence_vault.py +393 -285
  319. souleyez/ui/exploit_suggestions_view.py +555 -349
  320. souleyez/ui/export_view.py +100 -66
  321. souleyez/ui/gap_analysis_view.py +315 -171
  322. souleyez/ui/help_system.py +105 -97
  323. souleyez/ui/intelligence_view.py +436 -293
  324. souleyez/ui/interactive.py +22783 -10678
  325. souleyez/ui/interactive_selector.py +75 -68
  326. souleyez/ui/log_formatter.py +47 -39
  327. souleyez/ui/menu_components.py +22 -13
  328. souleyez/ui/msf_auxiliary_menu.py +184 -133
  329. souleyez/ui/pending_chains_view.py +336 -172
  330. souleyez/ui/progress_indicators.py +5 -3
  331. souleyez/ui/recommendations_view.py +195 -137
  332. souleyez/ui/rule_builder.py +343 -225
  333. souleyez/ui/setup_wizard.py +678 -284
  334. souleyez/ui/shortcuts.py +217 -165
  335. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  336. souleyez/ui/splunk_vulns_view.py +139 -86
  337. souleyez/ui/team_dashboard.py +498 -335
  338. souleyez/ui/template_selector.py +196 -105
  339. souleyez/ui/terminal.py +6 -6
  340. souleyez/ui/timeline_view.py +198 -127
  341. souleyez/ui/tool_setup.py +264 -164
  342. souleyez/ui/tutorial.py +202 -72
  343. souleyez/ui/tutorial_state.py +40 -40
  344. souleyez/ui/wazuh_vulns_view.py +235 -141
  345. souleyez/ui/wordlist_browser.py +260 -107
  346. souleyez/ui.py +464 -312
  347. souleyez/utils/tool_checker.py +427 -367
  348. souleyez/utils.py +33 -29
  349. souleyez/wordlists.py +134 -167
  350. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/METADATA +1 -1
  351. souleyez-2.43.32.dist-info/RECORD +441 -0
  352. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/WHEEL +1 -1
  353. souleyez-2.43.29.dist-info/RECORD +0 -379
  354. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/entry_points.txt +0 -0
  355. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/licenses/LICENSE +0 -0
  356. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Export deliverables in multiple formats (CSV, JSON, Markdown).
3
3
  """
4
+
4
5
  import csv
5
6
  import json
6
7
  from typing import Dict, List, Optional
@@ -13,379 +14,414 @@ from .engagements import EngagementManager
13
14
 
14
15
  class DeliverableExporter:
15
16
  """Export deliverables with evidence in various formats."""
16
-
17
+
17
18
  def __init__(self):
18
19
  self.db = get_db()
19
20
  self.dm = DeliverableManager()
20
21
  self.em = EvidenceManager()
21
22
  self.eng_mgr = EngagementManager()
22
-
23
+
23
24
  def export_csv(
24
- self,
25
- engagement_id: int,
26
- output_path: str,
27
- include_evidence: bool = True
25
+ self, engagement_id: int, output_path: str, include_evidence: bool = True
28
26
  ) -> bool:
29
27
  """
30
28
  Export deliverables to CSV format.
31
-
29
+
32
30
  Args:
33
31
  engagement_id: Target engagement
34
32
  output_path: Output file path
35
33
  include_evidence: Include evidence counts/details
36
-
34
+
37
35
  Returns:
38
36
  True if successful
39
37
  """
40
38
  deliverables = self.dm.list_deliverables(engagement_id)
41
39
  engagement = self.eng_mgr.get_by_id(engagement_id)
42
-
40
+
43
41
  if not deliverables:
44
42
  return False
45
-
46
- with open(output_path, 'w', newline='', encoding='utf-8') as f:
43
+
44
+ with open(output_path, "w", newline="", encoding="utf-8") as f:
47
45
  # Define CSV columns
48
46
  fieldnames = [
49
- 'ID',
50
- 'Category',
51
- 'Title',
52
- 'Description',
53
- 'Status',
54
- 'Priority',
55
- 'Target Type',
56
- 'Target Value',
57
- 'Current Value',
58
- 'Completion %',
59
- 'Started At',
60
- 'Completed At',
61
- 'Estimated Hours',
62
- 'Actual Hours',
63
- 'Blocker'
47
+ "ID",
48
+ "Category",
49
+ "Title",
50
+ "Description",
51
+ "Status",
52
+ "Priority",
53
+ "Target Type",
54
+ "Target Value",
55
+ "Current Value",
56
+ "Completion %",
57
+ "Started At",
58
+ "Completed At",
59
+ "Estimated Hours",
60
+ "Actual Hours",
61
+ "Blocker",
64
62
  ]
65
-
63
+
66
64
  if include_evidence:
67
- fieldnames.extend([
68
- 'Findings Count',
69
- 'Credentials Count',
70
- 'Screenshots Count',
71
- 'Jobs Count',
72
- 'Total Evidence'
73
- ])
74
-
65
+ fieldnames.extend(
66
+ [
67
+ "Findings Count",
68
+ "Credentials Count",
69
+ "Screenshots Count",
70
+ "Jobs Count",
71
+ "Total Evidence",
72
+ ]
73
+ )
74
+
75
75
  writer = csv.DictWriter(f, fieldnames=fieldnames)
76
76
  writer.writeheader()
77
-
77
+
78
78
  for d in deliverables:
79
79
  row = {
80
- 'ID': d['id'],
81
- 'Category': d['category'].replace('_', ' ').title(),
82
- 'Title': d['title'],
83
- 'Description': d.get('description', ''),
84
- 'Status': d['status'].replace('_', ' ').title(),
85
- 'Priority': d.get('priority', 'medium').title(),
86
- 'Target Type': d.get('target_type', 'manual').title(),
87
- 'Target Value': d.get('target_value', ''),
88
- 'Current Value': d.get('current_value', ''),
89
- 'Completion %': f"{d.get('completion_rate', 0) * 100:.0f}%",
90
- 'Started At': d.get('started_at', ''),
91
- 'Completed At': d.get('completed_at', ''),
92
- 'Estimated Hours': d.get('estimated_hours', 0),
93
- 'Actual Hours': d.get('actual_hours', 0),
94
- 'Blocker': d.get('blocker', '')
80
+ "ID": d["id"],
81
+ "Category": d["category"].replace("_", " ").title(),
82
+ "Title": d["title"],
83
+ "Description": d.get("description", ""),
84
+ "Status": d["status"].replace("_", " ").title(),
85
+ "Priority": d.get("priority", "medium").title(),
86
+ "Target Type": d.get("target_type", "manual").title(),
87
+ "Target Value": d.get("target_value", ""),
88
+ "Current Value": d.get("current_value", ""),
89
+ "Completion %": f"{d.get('completion_rate', 0) * 100:.0f}%",
90
+ "Started At": d.get("started_at", ""),
91
+ "Completed At": d.get("completed_at", ""),
92
+ "Estimated Hours": d.get("estimated_hours", 0),
93
+ "Actual Hours": d.get("actual_hours", 0),
94
+ "Blocker": d.get("blocker", ""),
95
95
  }
96
-
96
+
97
97
  if include_evidence:
98
- evidence = self.em.get_evidence(d['id'])
99
- row.update({
100
- 'Findings Count': len(evidence['findings']),
101
- 'Credentials Count': len(evidence['credentials']),
102
- 'Screenshots Count': len(evidence['screenshots']),
103
- 'Jobs Count': len(evidence['jobs']),
104
- 'Total Evidence': sum([
105
- len(evidence['findings']),
106
- len(evidence['credentials']),
107
- len(evidence['screenshots']),
108
- len(evidence['jobs'])
109
- ])
110
- })
111
-
98
+ evidence = self.em.get_evidence(d["id"])
99
+ row.update(
100
+ {
101
+ "Findings Count": len(evidence["findings"]),
102
+ "Credentials Count": len(evidence["credentials"]),
103
+ "Screenshots Count": len(evidence["screenshots"]),
104
+ "Jobs Count": len(evidence["jobs"]),
105
+ "Total Evidence": sum(
106
+ [
107
+ len(evidence["findings"]),
108
+ len(evidence["credentials"]),
109
+ len(evidence["screenshots"]),
110
+ len(evidence["jobs"]),
111
+ ]
112
+ ),
113
+ }
114
+ )
115
+
112
116
  writer.writerow(row)
113
-
117
+
114
118
  return True
115
-
119
+
116
120
  def export_json(
117
- self,
118
- engagement_id: int,
119
- output_path: str,
120
- include_evidence: bool = True
121
+ self, engagement_id: int, output_path: str, include_evidence: bool = True
121
122
  ) -> bool:
122
123
  """
123
124
  Export deliverables to JSON format.
124
-
125
+
125
126
  Args:
126
127
  engagement_id: Target engagement
127
128
  output_path: Output file path
128
129
  include_evidence: Include full evidence details
129
-
130
+
130
131
  Returns:
131
132
  True if successful
132
133
  """
133
134
  deliverables = self.dm.list_deliverables(engagement_id)
134
135
  engagement = self.eng_mgr.get_by_id(engagement_id)
135
136
  summary = self.dm.get_summary(engagement_id)
136
-
137
+
137
138
  export_data = {
138
- 'engagement': {
139
- 'id': engagement['id'],
140
- 'name': engagement['name'],
141
- 'description': engagement.get('description'),
142
- 'created_at': engagement.get('created_at'),
143
- 'engagement_type': engagement.get('engagement_type', 'network'),
144
- 'estimated_hours': engagement.get('estimated_hours', 0),
145
- 'actual_hours': engagement.get('actual_hours', 0)
139
+ "engagement": {
140
+ "id": engagement["id"],
141
+ "name": engagement["name"],
142
+ "description": engagement.get("description"),
143
+ "created_at": engagement.get("created_at"),
144
+ "engagement_type": engagement.get("engagement_type", "network"),
145
+ "estimated_hours": engagement.get("estimated_hours", 0),
146
+ "actual_hours": engagement.get("actual_hours", 0),
146
147
  },
147
- 'summary': {
148
- 'total_deliverables': summary['total'],
149
- 'completed': summary['completed'],
150
- 'in_progress': summary['in_progress'],
151
- 'pending': summary['pending'],
152
- 'completion_rate': f"{summary['completion_rate'] * 100:.1f}%"
148
+ "summary": {
149
+ "total_deliverables": summary["total"],
150
+ "completed": summary["completed"],
151
+ "in_progress": summary["in_progress"],
152
+ "pending": summary["pending"],
153
+ "completion_rate": f"{summary['completion_rate'] * 100:.1f}%",
153
154
  },
154
- 'deliverables': [],
155
- 'exported_at': datetime.now().isoformat()
155
+ "deliverables": [],
156
+ "exported_at": datetime.now().isoformat(),
156
157
  }
157
-
158
+
158
159
  for d in deliverables:
159
160
  deliverable_data = {
160
- 'id': d['id'],
161
- 'category': d['category'],
162
- 'title': d['title'],
163
- 'description': d.get('description'),
164
- 'status': d['status'],
165
- 'priority': d.get('priority', 'medium'),
166
- 'target_type': d.get('target_type', 'manual'),
167
- 'target_value': d.get('target_value'),
168
- 'current_value': d.get('current_value'),
169
- 'completion_rate': d.get('completion_rate', 0),
170
- 'auto_validate': d.get('auto_validate', False),
171
- 'validation_query': d.get('validation_query'),
172
- 'started_at': d.get('started_at'),
173
- 'completed_at': d.get('completed_at'),
174
- 'estimated_hours': d.get('estimated_hours', 0),
175
- 'actual_hours': d.get('actual_hours', 0),
176
- 'blocker': d.get('blocker'),
177
- 'assigned_to': d.get('assigned_to')
161
+ "id": d["id"],
162
+ "category": d["category"],
163
+ "title": d["title"],
164
+ "description": d.get("description"),
165
+ "status": d["status"],
166
+ "priority": d.get("priority", "medium"),
167
+ "target_type": d.get("target_type", "manual"),
168
+ "target_value": d.get("target_value"),
169
+ "current_value": d.get("current_value"),
170
+ "completion_rate": d.get("completion_rate", 0),
171
+ "auto_validate": d.get("auto_validate", False),
172
+ "validation_query": d.get("validation_query"),
173
+ "started_at": d.get("started_at"),
174
+ "completed_at": d.get("completed_at"),
175
+ "estimated_hours": d.get("estimated_hours", 0),
176
+ "actual_hours": d.get("actual_hours", 0),
177
+ "blocker": d.get("blocker"),
178
+ "assigned_to": d.get("assigned_to"),
178
179
  }
179
-
180
+
180
181
  if include_evidence:
181
- evidence = self.em.get_evidence(d['id'])
182
-
183
- deliverable_data['evidence'] = {
184
- 'findings': [
182
+ evidence = self.em.get_evidence(d["id"])
183
+
184
+ deliverable_data["evidence"] = {
185
+ "findings": [
185
186
  {
186
- 'id': f['id'],
187
- 'title': f.get('title'),
188
- 'severity': f.get('severity'),
189
- 'host': f.get('host'),
190
- 'link_notes': f.get('_link_notes'),
191
- 'linked_at': f.get('_linked_at')
192
- } for f in evidence['findings']
187
+ "id": f["id"],
188
+ "title": f.get("title"),
189
+ "severity": f.get("severity"),
190
+ "host": f.get("host"),
191
+ "link_notes": f.get("_link_notes"),
192
+ "linked_at": f.get("_linked_at"),
193
+ }
194
+ for f in evidence["findings"]
193
195
  ],
194
- 'credentials': [
196
+ "credentials": [
195
197
  {
196
- 'id': c['id'],
197
- 'username': c.get('username'),
198
- 'host': c.get('host'),
199
- 'credential_type': c.get('credential_type'),
200
- 'link_notes': c.get('_link_notes'),
201
- 'linked_at': c.get('_linked_at')
202
- } for c in evidence['credentials']
198
+ "id": c["id"],
199
+ "username": c.get("username"),
200
+ "host": c.get("host"),
201
+ "credential_type": c.get("credential_type"),
202
+ "link_notes": c.get("_link_notes"),
203
+ "linked_at": c.get("_linked_at"),
204
+ }
205
+ for c in evidence["credentials"]
203
206
  ],
204
- 'screenshots': [
207
+ "screenshots": [
205
208
  {
206
- 'id': s['id'],
207
- 'filename': s.get('filename'),
208
- 'description': s.get('description'),
209
- 'link_notes': s.get('_link_notes'),
210
- 'linked_at': s.get('_linked_at')
211
- } for s in evidence['screenshots']
209
+ "id": s["id"],
210
+ "filename": s.get("filename"),
211
+ "description": s.get("description"),
212
+ "link_notes": s.get("_link_notes"),
213
+ "linked_at": s.get("_linked_at"),
214
+ }
215
+ for s in evidence["screenshots"]
212
216
  ],
213
- 'jobs': [
217
+ "jobs": [
214
218
  {
215
- 'id': j['id'],
216
- 'tool': j.get('tool'),
217
- 'target': j.get('target'),
218
- 'status': j.get('status'),
219
- 'link_notes': j.get('_link_notes'),
220
- 'linked_at': j.get('_linked_at')
221
- } for j in evidence['jobs']
219
+ "id": j["id"],
220
+ "tool": j.get("tool"),
221
+ "target": j.get("target"),
222
+ "status": j.get("status"),
223
+ "link_notes": j.get("_link_notes"),
224
+ "linked_at": j.get("_linked_at"),
225
+ }
226
+ for j in evidence["jobs"]
222
227
  ],
223
- 'total_evidence': sum([
224
- len(evidence['findings']),
225
- len(evidence['credentials']),
226
- len(evidence['screenshots']),
227
- len(evidence['jobs'])
228
- ])
228
+ "total_evidence": sum(
229
+ [
230
+ len(evidence["findings"]),
231
+ len(evidence["credentials"]),
232
+ len(evidence["screenshots"]),
233
+ len(evidence["jobs"]),
234
+ ]
235
+ ),
229
236
  }
230
-
231
- export_data['deliverables'].append(deliverable_data)
232
-
233
- with open(output_path, 'w', encoding='utf-8') as f:
237
+
238
+ export_data["deliverables"].append(deliverable_data)
239
+
240
+ with open(output_path, "w", encoding="utf-8") as f:
234
241
  json.dump(export_data, f, indent=2, ensure_ascii=False)
235
-
242
+
236
243
  return True
237
-
244
+
238
245
  def export_markdown(
239
- self,
240
- engagement_id: int,
241
- output_path: str,
242
- include_evidence: bool = True
246
+ self, engagement_id: int, output_path: str, include_evidence: bool = True
243
247
  ) -> bool:
244
248
  """
245
249
  Export deliverables to Markdown format.
246
-
250
+
247
251
  Args:
248
252
  engagement_id: Target engagement
249
253
  output_path: Output file path
250
254
  include_evidence: Include evidence details
251
-
255
+
252
256
  Returns:
253
257
  True if successful
254
258
  """
255
259
  deliverables = self.dm.list_deliverables(engagement_id)
256
260
  engagement = self.eng_mgr.get_by_id(engagement_id)
257
261
  summary = self.dm.get_summary(engagement_id)
258
-
262
+
259
263
  if not deliverables:
260
264
  return False
261
-
262
- with open(output_path, 'w', encoding='utf-8') as f:
265
+
266
+ with open(output_path, "w", encoding="utf-8") as f:
263
267
  # Header
264
268
  f.write(f"# Deliverables Report: {engagement['name']}\n\n")
265
- f.write(f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
266
-
267
- if engagement.get('description'):
269
+ f.write(
270
+ f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
271
+ )
272
+
273
+ if engagement.get("description"):
268
274
  f.write(f"**Description:** {engagement['description']}\n\n")
269
-
275
+
270
276
  # Summary section
271
277
  f.write("## Summary\n\n")
272
278
  f.write(f"- **Total Deliverables:** {summary['total']}\n")
273
279
  f.write(f"- **Completed:** {summary['completed']}\n")
274
280
  f.write(f"- **In Progress:** {summary['in_progress']}\n")
275
281
  f.write(f"- **Pending:** {summary['pending']}\n")
276
- f.write(f"- **Completion Rate:** {summary['completion_rate'] * 100:.1f}%\n\n")
277
-
278
- if engagement.get('actual_hours'):
282
+ f.write(
283
+ f"- **Completion Rate:** {summary['completion_rate'] * 100:.1f}%\n\n"
284
+ )
285
+
286
+ if engagement.get("actual_hours"):
279
287
  f.write(f"- **Time Spent:** {engagement['actual_hours']:.1f} hours\n")
280
- if engagement.get('estimated_hours'):
281
- f.write(f"- **Estimated Time:** {engagement['estimated_hours']:.1f} hours\n")
282
-
288
+ if engagement.get("estimated_hours"):
289
+ f.write(
290
+ f"- **Estimated Time:** {engagement['estimated_hours']:.1f} hours\n"
291
+ )
292
+
283
293
  f.write("\n---\n\n")
284
-
294
+
285
295
  # Group by category
286
296
  categories = {}
287
297
  for d in deliverables:
288
- cat = d['category']
298
+ cat = d["category"]
289
299
  if cat not in categories:
290
300
  categories[cat] = []
291
301
  categories[cat].append(d)
292
-
302
+
293
303
  # Write deliverables by category
294
304
  category_names = {
295
- 'reconnaissance': 'Reconnaissance',
296
- 'enumeration': 'Enumeration',
297
- 'exploitation': 'Exploitation',
298
- 'post_exploitation': 'Post-Exploitation',
299
- 'techniques': 'Techniques'
305
+ "reconnaissance": "Reconnaissance",
306
+ "enumeration": "Enumeration",
307
+ "exploitation": "Exploitation",
308
+ "post_exploitation": "Post-Exploitation",
309
+ "techniques": "Techniques",
300
310
  }
301
-
302
- for category in ['reconnaissance', 'enumeration', 'exploitation', 'post_exploitation', 'techniques']:
311
+
312
+ for category in [
313
+ "reconnaissance",
314
+ "enumeration",
315
+ "exploitation",
316
+ "post_exploitation",
317
+ "techniques",
318
+ ]:
303
319
  if category not in categories:
304
320
  continue
305
-
321
+
306
322
  cat_deliverables = categories[category]
307
323
  cat_name = category_names.get(category, category.title())
308
-
324
+
309
325
  f.write(f"## {cat_name}\n\n")
310
-
326
+
311
327
  for d in cat_deliverables:
312
328
  # Status emoji
313
329
  status_emoji = {
314
- 'completed': '',
315
- 'in_progress': '🔄',
316
- 'pending': '',
317
- 'failed': ''
318
- }.get(d['status'], '')
319
-
330
+ "completed": "",
331
+ "in_progress": "🔄",
332
+ "pending": "",
333
+ "failed": "",
334
+ }.get(d["status"], "")
335
+
320
336
  # Priority badge
321
- priority = d.get('priority', 'medium')
337
+ priority = d.get("priority", "medium")
322
338
  priority_badge = {
323
- 'critical': '🔴 CRITICAL',
324
- 'high': '🟡 HIGH',
325
- 'medium': '🟢 MEDIUM',
326
- 'low': '⚪ LOW'
327
- }.get(priority, 'MEDIUM')
328
-
339
+ "critical": "🔴 CRITICAL",
340
+ "high": "🟡 HIGH",
341
+ "medium": "🟢 MEDIUM",
342
+ "low": "⚪ LOW",
343
+ }.get(priority, "MEDIUM")
344
+
329
345
  f.write(f"### {status_emoji} {d['title']}\n\n")
330
346
  f.write(f"**Priority:** {priority_badge} \n")
331
347
  f.write(f"**Status:** {d['status'].replace('_', ' ').title()} \n")
332
-
333
- if d.get('description'):
348
+
349
+ if d.get("description"):
334
350
  f.write(f"\n{d['description']}\n\n")
335
-
351
+
336
352
  # Progress info
337
- if d.get('target_type') == 'count':
338
- current = d.get('current_value', 0)
339
- target = d.get('target_value', 0)
353
+ if d.get("target_type") == "count":
354
+ current = d.get("current_value", 0)
355
+ target = d.get("target_value", 0)
340
356
  f.write(f"**Progress:** {current}/{target}\n\n")
341
- elif d.get('target_type') == 'boolean':
342
- status = '✓ Complete' if d['status'] == 'completed' else '✗ Incomplete'
357
+ elif d.get("target_type") == "boolean":
358
+ status = (
359
+ "✓ Complete"
360
+ if d["status"] == "completed"
361
+ else "✗ Incomplete"
362
+ )
343
363
  f.write(f"**Status:** {status}\n\n")
344
-
364
+
345
365
  # Time info
346
- if d.get('actual_hours'):
366
+ if d.get("actual_hours"):
347
367
  f.write(f"**Time Spent:** {d['actual_hours']:.1f}h\n\n")
348
-
368
+
349
369
  # Blocker
350
- if d.get('blocker'):
370
+ if d.get("blocker"):
351
371
  f.write(f"⚠️ **Blocker:** {d['blocker']}\n\n")
352
-
372
+
353
373
  # Evidence
354
374
  if include_evidence:
355
- evidence = self.em.get_evidence(d['id'])
356
- total_evidence = sum([
357
- len(evidence['findings']),
358
- len(evidence['credentials']),
359
- len(evidence['screenshots']),
360
- len(evidence['jobs'])
361
- ])
362
-
375
+ evidence = self.em.get_evidence(d["id"])
376
+ total_evidence = sum(
377
+ [
378
+ len(evidence["findings"]),
379
+ len(evidence["credentials"]),
380
+ len(evidence["screenshots"]),
381
+ len(evidence["jobs"]),
382
+ ]
383
+ )
384
+
363
385
  if total_evidence > 0:
364
386
  f.write("**Evidence:**\n\n")
365
-
366
- if evidence['findings']:
367
- f.write(f"- **Findings:** {len(evidence['findings'])}\n")
368
- for finding in evidence['findings'][:3]:
369
- severity = finding.get('severity', 'N/A').upper()
370
- f.write(f" - [{severity}] {finding.get('title', 'Unknown')}\n")
371
- if len(evidence['findings']) > 3:
372
- f.write(f" - ... and {len(evidence['findings']) - 3} more\n")
373
-
374
- if evidence['credentials']:
375
- f.write(f"- **Credentials:** {len(evidence['credentials'])}\n")
376
- for cred in evidence['credentials'][:3]:
377
- f.write(f" - {cred.get('username', 'N/A')}@{cred.get('host', 'N/A')}\n")
378
- if len(evidence['credentials']) > 3:
379
- f.write(f" - ... and {len(evidence['credentials']) - 3} more\n")
380
-
381
- if evidence['screenshots']:
382
- f.write(f"- **Screenshots:** {len(evidence['screenshots'])}\n")
383
-
384
- if evidence['jobs']:
387
+
388
+ if evidence["findings"]:
389
+ f.write(
390
+ f"- **Findings:** {len(evidence['findings'])}\n"
391
+ )
392
+ for finding in evidence["findings"][:3]:
393
+ severity = finding.get("severity", "N/A").upper()
394
+ f.write(
395
+ f" - [{severity}] {finding.get('title', 'Unknown')}\n"
396
+ )
397
+ if len(evidence["findings"]) > 3:
398
+ f.write(
399
+ f" - ... and {len(evidence['findings']) - 3} more\n"
400
+ )
401
+
402
+ if evidence["credentials"]:
403
+ f.write(
404
+ f"- **Credentials:** {len(evidence['credentials'])}\n"
405
+ )
406
+ for cred in evidence["credentials"][:3]:
407
+ f.write(
408
+ f" - {cred.get('username', 'N/A')}@{cred.get('host', 'N/A')}\n"
409
+ )
410
+ if len(evidence["credentials"]) > 3:
411
+ f.write(
412
+ f" - ... and {len(evidence['credentials']) - 3} more\n"
413
+ )
414
+
415
+ if evidence["screenshots"]:
416
+ f.write(
417
+ f"- **Screenshots:** {len(evidence['screenshots'])}\n"
418
+ )
419
+
420
+ if evidence["jobs"]:
385
421
  f.write(f"- **Jobs:** {len(evidence['jobs'])}\n")
386
-
422
+
387
423
  f.write("\n")
388
-
424
+
389
425
  f.write("---\n\n")
390
-
426
+
391
427
  return True