souleyez 2.43.26__py3-none-any.whl → 2.43.34__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 (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 +9526 -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 +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 +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 +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 +854 -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 +173 -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 +223 -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 +23434 -10286
  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.26.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
  353. souleyez-2.43.34.dist-info/RECORD +443 -0
  354. {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.26.dist-info/RECORD +0 -379
  356. {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,12 @@
1
1
  """
2
2
  CLI commands for deliverable tracking.
3
3
  """
4
+
4
5
  import click
5
6
  from rich.console import Console
6
7
  from rich.table import Table
7
8
  from rich.progress import Progress, BarColumn, TextColumn
9
+
8
10
  try:
9
11
  from rich.progress import TaskProgressColumn
10
12
  except ImportError:
@@ -25,7 +27,7 @@ def deliverables():
25
27
 
26
28
 
27
29
  @deliverables.command()
28
- @click.option('--defaults', is_flag=True, help='Create default deliverables')
30
+ @click.option("--defaults", is_flag=True, help="Create default deliverables")
29
31
  def init(defaults):
30
32
  """Initialize deliverables for current engagement."""
31
33
  em = EngagementManager()
@@ -37,15 +39,15 @@ def init(defaults):
37
39
  return
38
40
 
39
41
  if defaults:
40
- count = dm.create_default_deliverables(current['id'])
42
+ count = dm.create_default_deliverables(current["id"])
41
43
  console.print(f"[green]✓ Created {count} default deliverables[/green]")
42
44
  else:
43
45
  console.print("[yellow]Use --defaults to create default deliverables[/yellow]")
44
46
 
45
47
 
46
48
  @deliverables.command()
47
- @click.option('--category', '-c', help='Filter by category')
48
- @click.option('--status', '-s', help='Filter by status')
49
+ @click.option("--category", "-c", help="Filter by category")
50
+ @click.option("--status", "-s", help="Filter by status")
49
51
  def list(category, status):
50
52
  """List deliverables for current engagement."""
51
53
  em = EngagementManager()
@@ -56,21 +58,21 @@ def list(category, status):
56
58
  console.print("[red]No active engagement[/red]")
57
59
  return
58
60
 
59
- deliverables = dm.list_deliverables(
60
- current['id'],
61
- category=category,
62
- status=status
63
- )
61
+ deliverables = dm.list_deliverables(current["id"], category=category, status=status)
64
62
 
65
63
  if not deliverables:
66
64
  console.print("[yellow]No deliverables found[/yellow]")
67
- console.print("\nTip: Run 'souleyez deliverables init --defaults' to create default deliverables")
65
+ console.print(
66
+ "\nTip: Run 'souleyez deliverables init --defaults' to create default deliverables"
67
+ )
68
68
  return
69
69
 
70
- summary = dm.get_summary(current['id'])
70
+ summary = dm.get_summary(current["id"])
71
71
 
72
72
  console.print(f"\n[bold cyan]Deliverables - {current['name']}[/bold cyan]")
73
- console.print(f"Completion: {summary['completed']}/{summary['total']} ({summary['completion_rate']*100:.0f}%)\n")
73
+ console.print(
74
+ f"Completion: {summary['completed']}/{summary['total']} ({summary['completion_rate']*100:.0f}%)\n"
75
+ )
74
76
 
75
77
  table = Table()
76
78
  table.add_column("ID", style="cyan")
@@ -81,38 +83,33 @@ def list(category, status):
81
83
  table.add_column("Priority", style="red")
82
84
 
83
85
  for d in deliverables:
84
- if d['target_type'] == 'count':
85
- current_val = d['current_value'] or 0
86
- target_val = d['target_value']
86
+ if d["target_type"] == "count":
87
+ current_val = d["current_value"] or 0
88
+ target_val = d["target_value"]
87
89
  progress = f"{current_val}/{target_val}"
88
- elif d['target_type'] == 'boolean':
89
- progress = "✓" if d['status'] == 'completed' else "✗"
90
+ elif d["target_type"] == "boolean":
91
+ progress = "✓" if d["status"] == "completed" else "✗"
90
92
  else:
91
93
  progress = "Manual"
92
94
 
93
95
  status_emoji = {
94
- 'completed': '',
95
- 'in_progress': '🔄',
96
- 'pending': '⚠️',
97
- 'failed': ''
96
+ "completed": "",
97
+ "in_progress": "🔄",
98
+ "pending": "⚠️",
99
+ "failed": "",
98
100
  }
99
101
  status_str = f"{status_emoji.get(d['status'], '?')} {d['status']}"
100
102
 
101
103
  priority_color = {
102
- 'critical': '[red]',
103
- 'high': '[yellow]',
104
- 'medium': '[blue]',
105
- 'low': '[dim]'
104
+ "critical": "[red]",
105
+ "high": "[yellow]",
106
+ "medium": "[blue]",
107
+ "low": "[dim]",
106
108
  }
107
109
  priority_str = f"{priority_color.get(d['priority'], '')}{ d['priority']}[/]"
108
110
 
109
111
  table.add_row(
110
- str(d['id']),
111
- d['category'],
112
- d['title'],
113
- progress,
114
- status_str,
115
- priority_str
112
+ str(d["id"]), d["category"], d["title"], progress, status_str, priority_str
116
113
  )
117
114
 
118
115
  console.print(table)
@@ -132,7 +129,7 @@ def validate():
132
129
 
133
130
  console.print("[cyan]Validating deliverables...[/cyan]")
134
131
 
135
- stats = dm.validate_all(current['id'])
132
+ stats = dm.validate_all(current["id"])
136
133
 
137
134
  console.print(f"[green]✓ Validated {stats['updated']} deliverables[/green]")
138
135
  console.print(f" Completed: {stats['completed']}")
@@ -141,7 +138,7 @@ def validate():
141
138
 
142
139
 
143
140
  @deliverables.command()
144
- @click.argument('deliverable_id', type=int)
141
+ @click.argument("deliverable_id", type=int)
145
142
  def complete(deliverable_id):
146
143
  """Mark a deliverable as completed (manual deliverables)."""
147
144
  dm = DeliverableManager()
@@ -157,11 +154,15 @@ def complete(deliverable_id):
157
154
 
158
155
 
159
156
  @deliverables.command()
160
- @click.argument('category')
161
- @click.argument('title')
162
- @click.option('--target-type', '-t', default='manual', help='Target type (count, boolean, manual)')
163
- @click.option('--target-value', '-v', type=int, help='Target value (for count types)')
164
- @click.option('--priority', '-p', default='medium', help='Priority (critical, high, medium, low)')
157
+ @click.argument("category")
158
+ @click.argument("title")
159
+ @click.option(
160
+ "--target-type", "-t", default="manual", help="Target type (count, boolean, manual)"
161
+ )
162
+ @click.option("--target-value", "-v", type=int, help="Target value (for count types)")
163
+ @click.option(
164
+ "--priority", "-p", default="medium", help="Priority (critical, high, medium, low)"
165
+ )
165
166
  def add(category, title, target_type, target_value, priority):
166
167
  """Add a custom deliverable."""
167
168
  em = EngagementManager()
@@ -173,12 +174,12 @@ def add(category, title, target_type, target_value, priority):
173
174
  return
174
175
 
175
176
  deliverable_id = dm.add_deliverable(
176
- engagement_id=current['id'],
177
+ engagement_id=current["id"],
177
178
  category=category,
178
179
  title=title,
179
180
  target_type=target_type,
180
181
  target_value=target_value,
181
- priority=priority
182
+ priority=priority,
182
183
  )
183
184
 
184
185
  console.print(f"[green]✓ Deliverable added: ID {deliverable_id}[/green]")
@@ -186,8 +187,8 @@ def add(category, title, target_type, target_value, priority):
186
187
 
187
188
 
188
189
  @deliverables.command()
189
- @click.argument('deliverable_id', type=int)
190
- @click.confirmation_option(prompt='Are you sure you want to delete this deliverable?')
190
+ @click.argument("deliverable_id", type=int)
191
+ @click.confirmation_option(prompt="Are you sure you want to delete this deliverable?")
191
192
  def delete(deliverable_id):
192
193
  """Delete a deliverable."""
193
194
  dm = DeliverableManager()
@@ -209,11 +210,13 @@ def summary():
209
210
  console.print("[red]No active engagement[/red]")
210
211
  return
211
212
 
212
- summary = dm.get_summary(current['id'])
213
+ summary = dm.get_summary(current["id"])
213
214
 
214
215
  console.print(f"\n[bold cyan]Deliverable Summary - {current['name']}[/bold cyan]\n")
215
216
 
216
- console.print(f"[bold]Overall Progress:[/bold] {summary['completed']}/{summary['total']} ({summary['completion_rate']*100:.0f}%)")
217
+ console.print(
218
+ f"[bold]Overall Progress:[/bold] {summary['completed']}/{summary['total']} ({summary['completion_rate']*100:.0f}%)"
219
+ )
217
220
 
218
221
  columns = [
219
222
  TextColumn("[bold blue]{task.description}"),
@@ -225,17 +228,19 @@ def summary():
225
228
 
226
229
  with progress:
227
230
  task = progress.add_task(
228
- "Completion",
229
- total=summary['total'],
230
- completed=summary['completed']
231
+ "Completion", total=summary["total"], completed=summary["completed"]
231
232
  )
232
233
 
233
234
  console.print("\n[bold]By Category:[/bold]\n")
234
235
 
235
- for category, stats in summary['by_category'].items():
236
- completion_rate = stats['completed'] / stats['total'] if stats['total'] > 0 else 0
236
+ for category, stats in summary["by_category"].items():
237
+ completion_rate = (
238
+ stats["completed"] / stats["total"] if stats["total"] > 0 else 0
239
+ )
237
240
 
238
- console.print(f" {category.title()}: {stats['completed']}/{stats['total']} ({completion_rate*100:.0f}%)")
241
+ console.print(
242
+ f" {category.title()}: {stats['completed']}/{stats['total']} ({completion_rate*100:.0f}%)"
243
+ )
239
244
  console.print(f" ✅ Completed: {stats['completed']}")
240
245
  console.print(f" 🔄 In Progress: {stats['in_progress']}")
241
246
  console.print(f" ⚠️ Pending: {stats['pending']}")
@@ -7,13 +7,17 @@ Commands:
7
7
  - souleyez engagement team remove <name> <user> - Remove team member
8
8
  - souleyez engagement team transfer <name> <user> - Transfer ownership
9
9
  """
10
+
10
11
  import click
11
12
  from rich.console import Console
12
13
  from rich.table import Table
13
14
 
14
15
  from souleyez.security import require_login
15
16
  from souleyez.auth import get_current_user, Role, UserManager
16
- from souleyez.auth.engagement_access import EngagementAccessManager, EngagementPermission
17
+ from souleyez.auth.engagement_access import (
18
+ EngagementAccessManager,
19
+ EngagementPermission,
20
+ )
17
21
  from souleyez.storage.engagements import EngagementManager
18
22
  from souleyez.storage.database import get_db
19
23
 
@@ -39,12 +43,12 @@ def team_list(engagement_name):
39
43
  return
40
44
 
41
45
  # Check access
42
- if not em.can_access(eng['id']):
46
+ if not em.can_access(eng["id"]):
43
47
  console.print("[red]❌ You don't have access to this engagement[/red]")
44
48
  return
45
49
 
46
50
  access_mgr = EngagementAccessManager(get_db().db_path)
47
- members = access_mgr.get_team_members(eng['id'])
51
+ members = access_mgr.get_team_members(eng["id"])
48
52
 
49
53
  if not members:
50
54
  console.print(f"[yellow]No team members found for '{engagement_name}'[/yellow]")
@@ -57,17 +61,15 @@ def team_list(engagement_name):
57
61
  table.add_column("Added")
58
62
 
59
63
  for m in members:
60
- role_style = {
61
- 'owner': 'green bold',
62
- 'editor': 'cyan',
63
- 'viewer': 'dim'
64
- }.get(m['permission_level'], 'white')
64
+ role_style = {"owner": "green bold", "editor": "cyan", "viewer": "dim"}.get(
65
+ m["permission_level"], "white"
66
+ )
65
67
 
66
68
  table.add_row(
67
- m['username'],
68
- m['email'] or '-',
69
+ m["username"],
70
+ m["email"] or "-",
69
71
  f"[{role_style}]{m['permission_level'].upper()}[/{role_style}]",
70
- m['granted_at'][:10] if m['granted_at'] else '-'
72
+ m["granted_at"][:10] if m["granted_at"] else "-",
71
73
  )
72
74
 
73
75
  console.print(table)
@@ -78,10 +80,13 @@ def team_list(engagement_name):
78
80
  @require_login
79
81
  @click.argument("engagement_name")
80
82
  @click.argument("username")
81
- @click.option("--role", "-r",
82
- type=click.Choice(["editor", "viewer"]),
83
- default="viewer",
84
- help="Permission level (default: viewer)")
83
+ @click.option(
84
+ "--role",
85
+ "-r",
86
+ type=click.Choice(["editor", "viewer"]),
87
+ default="viewer",
88
+ help="Permission level (default: viewer)",
89
+ )
85
90
  def team_add(engagement_name, username, role):
86
91
  """Add a user to an engagement's team."""
87
92
  em = EngagementManager()
@@ -95,8 +100,10 @@ def team_add(engagement_name, username, role):
95
100
  access_mgr = EngagementAccessManager(get_db().db_path)
96
101
  user = get_current_user()
97
102
 
98
- if not access_mgr.can_manage_team(eng['id'], user.id, user.role):
99
- console.print("[red]❌ Only the engagement owner or admin can manage team members[/red]")
103
+ if not access_mgr.can_manage_team(eng["id"], user.id, user.role):
104
+ console.print(
105
+ "[red]❌ Only the engagement owner or admin can manage team members[/red]"
106
+ )
100
107
  return
101
108
 
102
109
  # Find target user
@@ -108,11 +115,15 @@ def team_add(engagement_name, username, role):
108
115
  return
109
116
 
110
117
  # Add team member
111
- perm = EngagementPermission.EDITOR if role == "editor" else EngagementPermission.VIEWER
112
- success, error = access_mgr.add_team_member(eng['id'], target.id, perm, user.id)
118
+ perm = (
119
+ EngagementPermission.EDITOR if role == "editor" else EngagementPermission.VIEWER
120
+ )
121
+ success, error = access_mgr.add_team_member(eng["id"], target.id, perm, user.id)
113
122
 
114
123
  if success:
115
- console.print(f"[green]✅ Added {username} as {role} to '{engagement_name}'[/green]")
124
+ console.print(
125
+ f"[green]✅ Added {username} as {role} to '{engagement_name}'[/green]"
126
+ )
116
127
  else:
117
128
  console.print(f"[red]❌ Failed: {error}[/red]")
118
129
 
@@ -133,8 +144,10 @@ def team_remove(engagement_name, username):
133
144
  access_mgr = EngagementAccessManager(get_db().db_path)
134
145
  user = get_current_user()
135
146
 
136
- if not access_mgr.can_manage_team(eng['id'], user.id, user.role):
137
- console.print("[red]❌ Only the engagement owner or admin can manage team members[/red]")
147
+ if not access_mgr.can_manage_team(eng["id"], user.id, user.role):
148
+ console.print(
149
+ "[red]❌ Only the engagement owner or admin can manage team members[/red]"
150
+ )
138
151
  return
139
152
 
140
153
  user_mgr = UserManager(get_db().db_path)
@@ -144,7 +157,7 @@ def team_remove(engagement_name, username):
144
157
  console.print(f"[red]❌ User '{username}' not found[/red]")
145
158
  return
146
159
 
147
- success, error = access_mgr.remove_team_member(eng['id'], target.id)
160
+ success, error = access_mgr.remove_team_member(eng["id"], target.id)
148
161
 
149
162
  if success:
150
163
  console.print(f"[green]✅ Removed {username} from '{engagement_name}'[/green]")
@@ -170,8 +183,10 @@ def team_transfer(engagement_name, new_owner_username, force):
170
183
  user = get_current_user()
171
184
 
172
185
  # Only owner or admin can transfer
173
- if not access_mgr.can_manage_team(eng['id'], user.id, user.role):
174
- console.print("[red]❌ Only the engagement owner or admin can transfer ownership[/red]")
186
+ if not access_mgr.can_manage_team(eng["id"], user.id, user.role):
187
+ console.print(
188
+ "[red]❌ Only the engagement owner or admin can transfer ownership[/red]"
189
+ )
175
190
  return
176
191
 
177
192
  user_mgr = UserManager(get_db().db_path)
@@ -182,14 +197,18 @@ def team_transfer(engagement_name, new_owner_username, force):
182
197
  return
183
198
 
184
199
  if not force:
185
- if not click.confirm(f"Transfer ownership of '{engagement_name}' to {new_owner_username}?"):
200
+ if not click.confirm(
201
+ f"Transfer ownership of '{engagement_name}' to {new_owner_username}?"
202
+ ):
186
203
  console.print("[yellow]Cancelled[/yellow]")
187
204
  return
188
205
 
189
- success, error = access_mgr.transfer_ownership(eng['id'], new_owner.id, user.id)
206
+ success, error = access_mgr.transfer_ownership(eng["id"], new_owner.id, user.id)
190
207
 
191
208
  if success:
192
- console.print(f"[green]✅ Ownership transferred to {new_owner_username}[/green]")
209
+ console.print(
210
+ f"[green]✅ Ownership transferred to {new_owner_username}[/green]"
211
+ )
193
212
  console.print(f" You have been added as an editor.")
194
213
  else:
195
214
  console.print(f"[red]❌ Failed: {error}[/red]")
@@ -18,13 +18,13 @@ def license():
18
18
 
19
19
 
20
20
  @license.command()
21
- @click.argument('license_key')
21
+ @click.argument("license_key")
22
22
  def activate(license_key: str):
23
23
  """Activate a Pro license key."""
24
24
  from souleyez.licensing import activate_license, validate_license
25
25
 
26
26
  click.echo()
27
- click.echo(click.style(" Activating license...", fg='cyan'))
27
+ click.echo(click.style(" Activating license...", fg="cyan"))
28
28
 
29
29
  # Validate and save
30
30
  success, message = activate_license(license_key)
@@ -32,7 +32,9 @@ def activate(license_key: str):
32
32
  if success:
33
33
  info = validate_license(license_key)
34
34
  click.echo()
35
- click.echo(click.style(" License activated successfully!", fg='green', bold=True))
35
+ click.echo(
36
+ click.style(" License activated successfully!", fg="green", bold=True)
37
+ )
36
38
  click.echo()
37
39
  click.echo(f" Email: {info.email}")
38
40
  click.echo(f" Tier: {click.style(info.tier, fg='magenta', bold=True)}")
@@ -40,17 +42,19 @@ def activate(license_key: str):
40
42
  if info.expires_at:
41
43
  days = info.days_remaining
42
44
  if days > 30:
43
- color = 'green'
45
+ color = "green"
44
46
  elif days > 7:
45
- color = 'yellow'
47
+ color = "yellow"
46
48
  else:
47
- color = 'red'
48
- click.echo(f" Expires: {info.expires_at.strftime('%Y-%m-%d')} ({click.style(f'{days} days', fg=color)})")
49
+ color = "red"
50
+ click.echo(
51
+ f" Expires: {info.expires_at.strftime('%Y-%m-%d')} ({click.style(f'{days} days', fg=color)})"
52
+ )
49
53
  else:
50
54
  click.echo(f" Expires: {click.style('Never (perpetual)', fg='green')}")
51
55
 
52
56
  click.echo()
53
- click.echo(click.style(" Pro features are now unlocked!", fg='cyan'))
57
+ click.echo(click.style(" Pro features are now unlocked!", fg="cyan"))
54
58
  click.echo()
55
59
 
56
60
  # Update user tier if auth system is in use
@@ -65,16 +69,17 @@ def activate(license_key: str):
65
69
  user_mgr.set_user_tier(
66
70
  user.id,
67
71
  Tier.PRO,
68
- license_key=license_key[:20] + "...", # Store truncated key as reference
72
+ license_key=license_key[:20]
73
+ + "...", # Store truncated key as reference
69
74
  expires_at=info.expires_at,
70
- _bypass_validation=True # Already validated by licensing module
75
+ _bypass_validation=True, # Already validated by licensing module
71
76
  )
72
77
  except Exception:
73
78
  pass # Auth system not in use
74
79
 
75
80
  else:
76
81
  click.echo()
77
- click.echo(click.style(f" License activation failed: {message}", fg='red'))
82
+ click.echo(click.style(f" License activation failed: {message}", fg="red"))
78
83
  click.echo()
79
84
  click.echo(" Please check your license key and try again.")
80
85
  click.echo(" Contact support if the problem persists.")
@@ -90,7 +95,7 @@ def status():
90
95
  info = get_active_license()
91
96
 
92
97
  if info is None:
93
- click.echo(click.style(" No active license", fg='yellow'))
98
+ click.echo(click.style(" No active license", fg="yellow"))
94
99
  click.echo()
95
100
  click.echo(" You are using the FREE tier.")
96
101
  click.echo(" Upgrade to Pro: https://www.cybersoulsecurity.com/souleyez")
@@ -100,14 +105,14 @@ def status():
100
105
  return
101
106
 
102
107
  if not info.is_valid:
103
- click.echo(click.style(f" License invalid: {info.error}", fg='red'))
108
+ click.echo(click.style(f" License invalid: {info.error}", fg="red"))
104
109
  click.echo()
105
110
  click.echo(" Please reactivate or contact support.")
106
111
  click.echo()
107
112
  return
108
113
 
109
114
  # Valid license
110
- click.echo(click.style(" License Status: ACTIVE", fg='green', bold=True))
115
+ click.echo(click.style(" License Status: ACTIVE", fg="green", bold=True))
111
116
  click.echo()
112
117
  click.echo(f" Email: {info.email}")
113
118
  click.echo(f" Tier: {click.style(info.tier, fg='magenta', bold=True)}")
@@ -115,12 +120,14 @@ def status():
115
120
  if info.expires_at:
116
121
  days = info.days_remaining
117
122
  if days > 30:
118
- color = 'green'
123
+ color = "green"
119
124
  elif days > 7:
120
- color = 'yellow'
125
+ color = "yellow"
121
126
  else:
122
- color = 'red'
123
- click.echo(f" Expires: {info.expires_at.strftime('%Y-%m-%d')} ({click.style(f'{days} days remaining', fg=color)})")
127
+ color = "red"
128
+ click.echo(
129
+ f" Expires: {info.expires_at.strftime('%Y-%m-%d')} ({click.style(f'{days} days remaining', fg=color)})"
130
+ )
124
131
  else:
125
132
  click.echo(f" Expires: {click.style('Never (perpetual)', fg='green')}")
126
133
 
@@ -138,13 +145,15 @@ def deactivate():
138
145
  info = get_active_license()
139
146
  if info is None:
140
147
  click.echo()
141
- click.echo(click.style(" No active license to remove.", fg='yellow'))
148
+ click.echo(click.style(" No active license to remove.", fg="yellow"))
142
149
  click.echo()
143
150
  return
144
151
 
145
152
  # Confirm
146
153
  click.echo()
147
- click.echo(click.style(" Warning: This will remove your Pro license.", fg='yellow'))
154
+ click.echo(
155
+ click.style(" Warning: This will remove your Pro license.", fg="yellow")
156
+ )
148
157
  click.echo(f" License: {info.email}")
149
158
  click.echo()
150
159
 
@@ -154,7 +163,7 @@ def deactivate():
154
163
 
155
164
  if deactivate_license():
156
165
  click.echo()
157
- click.echo(click.style(" License removed.", fg='green'))
166
+ click.echo(click.style(" License removed.", fg="green"))
158
167
  click.echo(" You are now on the FREE tier.")
159
168
  click.echo()
160
169
 
@@ -171,11 +180,11 @@ def deactivate():
171
180
  pass # Auth system not in use
172
181
  else:
173
182
  click.echo()
174
- click.echo(click.style(" Failed to remove license.", fg='red'))
183
+ click.echo(click.style(" Failed to remove license.", fg="red"))
175
184
  click.echo()
176
185
 
177
186
 
178
- @license.command('machine-id')
187
+ @license.command("machine-id")
179
188
  def machine_id():
180
189
  """Show this machine's ID for hardware-bound licenses."""
181
190
  from souleyez.licensing.validator import get_machine_id