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.
- souleyez/__init__.py +1 -2
- souleyez/ai/__init__.py +21 -15
- souleyez/ai/action_mapper.py +249 -150
- souleyez/ai/chain_advisor.py +116 -100
- souleyez/ai/claude_provider.py +29 -28
- souleyez/ai/context_builder.py +80 -62
- souleyez/ai/executor.py +158 -117
- souleyez/ai/feedback_handler.py +136 -121
- souleyez/ai/llm_factory.py +27 -20
- souleyez/ai/llm_provider.py +4 -2
- souleyez/ai/ollama_provider.py +6 -9
- souleyez/ai/ollama_service.py +44 -37
- souleyez/ai/path_scorer.py +91 -76
- souleyez/ai/recommender.py +176 -144
- souleyez/ai/report_context.py +74 -73
- souleyez/ai/report_service.py +84 -66
- souleyez/ai/result_parser.py +222 -229
- souleyez/ai/safety.py +67 -44
- souleyez/auth/__init__.py +23 -22
- souleyez/auth/audit.py +36 -26
- souleyez/auth/engagement_access.py +65 -48
- souleyez/auth/permissions.py +14 -3
- souleyez/auth/session_manager.py +54 -37
- souleyez/auth/user_manager.py +109 -64
- souleyez/commands/audit.py +40 -43
- souleyez/commands/auth.py +35 -15
- souleyez/commands/deliverables.py +55 -50
- souleyez/commands/engagement.py +47 -28
- souleyez/commands/license.py +32 -23
- souleyez/commands/screenshots.py +36 -32
- souleyez/commands/user.py +82 -36
- souleyez/config.py +52 -44
- souleyez/core/credential_tester.py +87 -81
- souleyez/core/cve_mappings.py +179 -192
- souleyez/core/cve_matcher.py +162 -148
- souleyez/core/msf_auto_mapper.py +100 -83
- souleyez/core/msf_chain_engine.py +294 -256
- souleyez/core/msf_database.py +153 -70
- souleyez/core/msf_integration.py +679 -673
- souleyez/core/msf_rpc_client.py +40 -42
- souleyez/core/msf_rpc_manager.py +77 -79
- souleyez/core/msf_sync_manager.py +241 -181
- souleyez/core/network_utils.py +22 -15
- souleyez/core/parser_handler.py +34 -25
- souleyez/core/pending_chains.py +114 -63
- souleyez/core/templates.py +158 -107
- souleyez/core/tool_chaining.py +9564 -2881
- souleyez/core/version_utils.py +79 -94
- souleyez/core/vuln_correlation.py +136 -89
- souleyez/core/web_utils.py +33 -32
- souleyez/data/wordlists/ad_users.txt +378 -0
- souleyez/data/wordlists/api_endpoints_large.txt +769 -0
- souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
- souleyez/data/wordlists/lfi_payloads.txt +82 -0
- souleyez/data/wordlists/passwords_brute.txt +1548 -0
- souleyez/data/wordlists/passwords_crack.txt +2479 -0
- souleyez/data/wordlists/passwords_spray.txt +386 -0
- souleyez/data/wordlists/subdomains_large.txt +5057 -0
- souleyez/data/wordlists/usernames_common.txt +694 -0
- souleyez/data/wordlists/web_dirs_large.txt +4769 -0
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +12 -17
- souleyez/detection/mitre_mappings.py +61 -55
- souleyez/detection/validator.py +97 -86
- souleyez/devtools.py +23 -10
- souleyez/docs/README.md +4 -4
- souleyez/docs/api-reference/cli-commands.md +2 -2
- souleyez/docs/developer-guide/adding-new-tools.md +562 -0
- souleyez/docs/user-guide/auto-chaining.md +30 -8
- souleyez/docs/user-guide/getting-started.md +1 -1
- souleyez/docs/user-guide/installation.md +26 -3
- souleyez/docs/user-guide/metasploit-integration.md +2 -2
- souleyez/docs/user-guide/rbac.md +1 -1
- souleyez/docs/user-guide/scope-management.md +1 -1
- souleyez/docs/user-guide/siem-integration.md +1 -1
- souleyez/docs/user-guide/tools-reference.md +1 -8
- souleyez/docs/user-guide/worker-management.md +1 -1
- souleyez/engine/background.py +1239 -535
- souleyez/engine/base.py +4 -1
- souleyez/engine/job_status.py +17 -49
- souleyez/engine/log_sanitizer.py +103 -77
- souleyez/engine/manager.py +38 -7
- souleyez/engine/result_handler.py +2200 -1550
- souleyez/engine/worker_manager.py +50 -41
- souleyez/export/evidence_bundle.py +72 -62
- souleyez/feature_flags/features.py +16 -20
- souleyez/feature_flags.py +5 -9
- souleyez/handlers/__init__.py +11 -0
- souleyez/handlers/base.py +188 -0
- souleyez/handlers/bash_handler.py +277 -0
- souleyez/handlers/bloodhound_handler.py +243 -0
- souleyez/handlers/certipy_handler.py +311 -0
- souleyez/handlers/crackmapexec_handler.py +486 -0
- souleyez/handlers/dnsrecon_handler.py +344 -0
- souleyez/handlers/enum4linux_handler.py +400 -0
- souleyez/handlers/evil_winrm_handler.py +493 -0
- souleyez/handlers/ffuf_handler.py +815 -0
- souleyez/handlers/gobuster_handler.py +1114 -0
- souleyez/handlers/gpp_extract_handler.py +334 -0
- souleyez/handlers/hashcat_handler.py +444 -0
- souleyez/handlers/hydra_handler.py +564 -0
- souleyez/handlers/impacket_getuserspns_handler.py +343 -0
- souleyez/handlers/impacket_psexec_handler.py +222 -0
- souleyez/handlers/impacket_secretsdump_handler.py +426 -0
- souleyez/handlers/john_handler.py +286 -0
- souleyez/handlers/katana_handler.py +425 -0
- souleyez/handlers/kerbrute_handler.py +298 -0
- souleyez/handlers/ldapsearch_handler.py +636 -0
- souleyez/handlers/lfi_extract_handler.py +464 -0
- souleyez/handlers/msf_auxiliary_handler.py +409 -0
- souleyez/handlers/msf_exploit_handler.py +380 -0
- souleyez/handlers/nikto_handler.py +413 -0
- souleyez/handlers/nmap_handler.py +821 -0
- souleyez/handlers/nuclei_handler.py +359 -0
- souleyez/handlers/nxc_handler.py +417 -0
- souleyez/handlers/rdp_sec_check_handler.py +353 -0
- souleyez/handlers/registry.py +292 -0
- souleyez/handlers/responder_handler.py +232 -0
- souleyez/handlers/service_explorer_handler.py +434 -0
- souleyez/handlers/smbclient_handler.py +344 -0
- souleyez/handlers/smbmap_handler.py +510 -0
- souleyez/handlers/smbpasswd_handler.py +296 -0
- souleyez/handlers/sqlmap_handler.py +1116 -0
- souleyez/handlers/theharvester_handler.py +601 -0
- souleyez/handlers/web_login_test_handler.py +327 -0
- souleyez/handlers/whois_handler.py +277 -0
- souleyez/handlers/wpscan_handler.py +554 -0
- souleyez/history.py +32 -16
- souleyez/importers/msf_importer.py +106 -75
- souleyez/importers/smart_importer.py +208 -147
- souleyez/integrations/siem/__init__.py +10 -10
- souleyez/integrations/siem/base.py +17 -18
- souleyez/integrations/siem/elastic.py +108 -122
- souleyez/integrations/siem/factory.py +207 -80
- souleyez/integrations/siem/googlesecops.py +146 -154
- souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
- souleyez/integrations/siem/sentinel.py +107 -109
- souleyez/integrations/siem/splunk.py +246 -212
- souleyez/integrations/siem/wazuh.py +65 -71
- souleyez/integrations/wazuh/__init__.py +5 -5
- souleyez/integrations/wazuh/client.py +70 -93
- souleyez/integrations/wazuh/config.py +85 -57
- souleyez/integrations/wazuh/host_mapper.py +28 -36
- souleyez/integrations/wazuh/sync.py +78 -68
- souleyez/intelligence/__init__.py +4 -5
- souleyez/intelligence/correlation_analyzer.py +309 -295
- souleyez/intelligence/exploit_knowledge.py +661 -623
- souleyez/intelligence/exploit_suggestions.py +159 -139
- souleyez/intelligence/gap_analyzer.py +132 -97
- souleyez/intelligence/gap_detector.py +251 -214
- souleyez/intelligence/sensitive_tables.py +266 -129
- souleyez/intelligence/service_parser.py +137 -123
- souleyez/intelligence/surface_analyzer.py +407 -268
- souleyez/intelligence/target_parser.py +159 -162
- souleyez/licensing/__init__.py +6 -6
- souleyez/licensing/validator.py +17 -19
- souleyez/log_config.py +79 -54
- souleyez/main.py +1505 -687
- souleyez/migrations/fix_job_counter.py +16 -14
- souleyez/parsers/bloodhound_parser.py +41 -39
- souleyez/parsers/crackmapexec_parser.py +178 -111
- souleyez/parsers/dalfox_parser.py +72 -77
- souleyez/parsers/dnsrecon_parser.py +103 -91
- souleyez/parsers/enum4linux_parser.py +183 -153
- souleyez/parsers/ffuf_parser.py +29 -25
- souleyez/parsers/gobuster_parser.py +301 -41
- souleyez/parsers/hashcat_parser.py +324 -79
- souleyez/parsers/http_fingerprint_parser.py +350 -103
- souleyez/parsers/hydra_parser.py +131 -111
- souleyez/parsers/impacket_parser.py +231 -178
- souleyez/parsers/john_parser.py +98 -86
- souleyez/parsers/katana_parser.py +316 -0
- souleyez/parsers/msf_parser.py +943 -498
- souleyez/parsers/nikto_parser.py +346 -65
- souleyez/parsers/nmap_parser.py +262 -174
- souleyez/parsers/nuclei_parser.py +40 -44
- souleyez/parsers/responder_parser.py +26 -26
- souleyez/parsers/searchsploit_parser.py +74 -74
- souleyez/parsers/service_explorer_parser.py +279 -0
- souleyez/parsers/smbmap_parser.py +180 -124
- souleyez/parsers/sqlmap_parser.py +434 -308
- souleyez/parsers/theharvester_parser.py +75 -57
- souleyez/parsers/whois_parser.py +135 -94
- souleyez/parsers/wpscan_parser.py +278 -190
- souleyez/plugins/afp.py +44 -36
- souleyez/plugins/afp_brute.py +114 -46
- souleyez/plugins/ard.py +48 -37
- souleyez/plugins/bloodhound.py +95 -61
- souleyez/plugins/certipy.py +303 -0
- souleyez/plugins/crackmapexec.py +186 -85
- souleyez/plugins/dalfox.py +120 -59
- souleyez/plugins/dns_hijack.py +146 -41
- souleyez/plugins/dnsrecon.py +97 -61
- souleyez/plugins/enum4linux.py +91 -66
- souleyez/plugins/evil_winrm.py +291 -0
- souleyez/plugins/ffuf.py +166 -90
- souleyez/plugins/firmware_extract.py +133 -29
- souleyez/plugins/gobuster.py +387 -190
- souleyez/plugins/gpp_extract.py +393 -0
- souleyez/plugins/hashcat.py +100 -73
- souleyez/plugins/http_fingerprint.py +913 -267
- souleyez/plugins/hydra.py +566 -200
- souleyez/plugins/impacket_getnpusers.py +117 -69
- souleyez/plugins/impacket_psexec.py +84 -64
- souleyez/plugins/impacket_secretsdump.py +103 -69
- souleyez/plugins/impacket_smbclient.py +89 -75
- souleyez/plugins/john.py +86 -69
- souleyez/plugins/katana.py +313 -0
- souleyez/plugins/kerbrute.py +237 -0
- souleyez/plugins/lfi_extract.py +541 -0
- souleyez/plugins/macos_ssh.py +117 -48
- souleyez/plugins/mdns.py +35 -30
- souleyez/plugins/msf_auxiliary.py +253 -130
- souleyez/plugins/msf_exploit.py +239 -161
- souleyez/plugins/nikto.py +134 -78
- souleyez/plugins/nmap.py +275 -91
- souleyez/plugins/nuclei.py +180 -89
- souleyez/plugins/nxc.py +285 -0
- souleyez/plugins/plugin_base.py +35 -36
- souleyez/plugins/plugin_template.py +13 -5
- souleyez/plugins/rdp_sec_check.py +130 -0
- souleyez/plugins/responder.py +112 -71
- souleyez/plugins/router_http_brute.py +76 -65
- souleyez/plugins/router_ssh_brute.py +118 -41
- souleyez/plugins/router_telnet_brute.py +124 -42
- souleyez/plugins/routersploit.py +91 -59
- souleyez/plugins/routersploit_exploit.py +77 -55
- souleyez/plugins/searchsploit.py +91 -77
- souleyez/plugins/service_explorer.py +1160 -0
- souleyez/plugins/smbmap.py +122 -72
- souleyez/plugins/smbpasswd.py +215 -0
- souleyez/plugins/sqlmap.py +301 -113
- souleyez/plugins/theharvester.py +127 -75
- souleyez/plugins/tr069.py +79 -57
- souleyez/plugins/upnp.py +65 -47
- souleyez/plugins/upnp_abuse.py +73 -55
- souleyez/plugins/vnc_access.py +129 -42
- souleyez/plugins/vnc_brute.py +109 -38
- souleyez/plugins/web_login_test.py +417 -0
- souleyez/plugins/whois.py +77 -58
- souleyez/plugins/wpscan.py +219 -69
- souleyez/reporting/__init__.py +2 -1
- souleyez/reporting/attack_chain.py +411 -346
- souleyez/reporting/charts.py +436 -501
- souleyez/reporting/compliance_mappings.py +334 -201
- souleyez/reporting/detection_report.py +126 -125
- souleyez/reporting/formatters.py +828 -591
- souleyez/reporting/generator.py +386 -302
- souleyez/reporting/metrics.py +72 -75
- souleyez/scanner.py +35 -29
- souleyez/security/__init__.py +37 -11
- souleyez/security/scope_validator.py +175 -106
- souleyez/security/validation.py +237 -149
- souleyez/security.py +22 -6
- souleyez/storage/credentials.py +247 -186
- souleyez/storage/crypto.py +296 -129
- souleyez/storage/database.py +73 -50
- souleyez/storage/db.py +58 -36
- souleyez/storage/deliverable_evidence.py +177 -128
- souleyez/storage/deliverable_exporter.py +282 -246
- souleyez/storage/deliverable_templates.py +134 -116
- souleyez/storage/deliverables.py +135 -130
- souleyez/storage/engagements.py +109 -56
- souleyez/storage/evidence.py +181 -152
- souleyez/storage/execution_log.py +31 -17
- souleyez/storage/exploit_attempts.py +93 -57
- souleyez/storage/exploits.py +67 -36
- souleyez/storage/findings.py +48 -61
- souleyez/storage/hosts.py +176 -144
- souleyez/storage/migrate_to_engagements.py +43 -19
- souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
- souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
- souleyez/storage/migrations/_003_add_execution_log.py +14 -8
- souleyez/storage/migrations/_005_screenshots.py +13 -5
- souleyez/storage/migrations/_006_deliverables.py +13 -5
- souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
- souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
- souleyez/storage/migrations/_010_evidence_linking.py +17 -10
- souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
- souleyez/storage/migrations/_012_team_collaboration.py +34 -21
- souleyez/storage/migrations/_013_add_host_tags.py +12 -6
- souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
- souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
- souleyez/storage/migrations/_016_add_domain_field.py +10 -4
- souleyez/storage/migrations/_017_msf_sessions.py +16 -8
- souleyez/storage/migrations/_018_add_osint_target.py +10 -6
- souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
- souleyez/storage/migrations/_020_add_rbac.py +36 -15
- souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
- souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
- souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
- souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
- souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
- souleyez/storage/migrations/__init__.py +26 -26
- souleyez/storage/migrations/migration_manager.py +19 -19
- souleyez/storage/msf_sessions.py +100 -65
- souleyez/storage/osint.py +17 -24
- souleyez/storage/recommendation_engine.py +269 -235
- souleyez/storage/screenshots.py +33 -32
- souleyez/storage/smb_shares.py +136 -92
- souleyez/storage/sqlmap_data.py +183 -128
- souleyez/storage/team_collaboration.py +135 -141
- souleyez/storage/timeline_tracker.py +122 -94
- souleyez/storage/wazuh_vulns.py +64 -66
- souleyez/storage/web_paths.py +33 -37
- souleyez/testing/credential_tester.py +221 -205
- souleyez/ui/__init__.py +1 -1
- souleyez/ui/ai_quotes.py +12 -12
- souleyez/ui/attack_surface.py +2439 -1516
- souleyez/ui/chain_rules_view.py +914 -382
- souleyez/ui/correlation_view.py +312 -230
- souleyez/ui/dashboard.py +2382 -1130
- souleyez/ui/deliverables_view.py +148 -62
- souleyez/ui/design_system.py +13 -13
- souleyez/ui/errors.py +49 -49
- souleyez/ui/evidence_linking_view.py +284 -179
- souleyez/ui/evidence_vault.py +393 -285
- souleyez/ui/exploit_suggestions_view.py +555 -349
- souleyez/ui/export_view.py +100 -66
- souleyez/ui/gap_analysis_view.py +315 -171
- souleyez/ui/help_system.py +105 -97
- souleyez/ui/intelligence_view.py +436 -293
- souleyez/ui/interactive.py +23034 -10679
- souleyez/ui/interactive_selector.py +75 -68
- souleyez/ui/log_formatter.py +47 -39
- souleyez/ui/menu_components.py +22 -13
- souleyez/ui/msf_auxiliary_menu.py +184 -133
- souleyez/ui/pending_chains_view.py +336 -172
- souleyez/ui/progress_indicators.py +5 -3
- souleyez/ui/recommendations_view.py +195 -137
- souleyez/ui/rule_builder.py +343 -225
- souleyez/ui/setup_wizard.py +678 -284
- souleyez/ui/shortcuts.py +217 -165
- souleyez/ui/splunk_gap_analysis_view.py +452 -270
- souleyez/ui/splunk_vulns_view.py +139 -86
- souleyez/ui/team_dashboard.py +498 -335
- souleyez/ui/template_selector.py +196 -105
- souleyez/ui/terminal.py +6 -6
- souleyez/ui/timeline_view.py +198 -127
- souleyez/ui/tool_setup.py +264 -164
- souleyez/ui/tutorial.py +202 -72
- souleyez/ui/tutorial_state.py +40 -40
- souleyez/ui/wazuh_vulns_view.py +235 -141
- souleyez/ui/wordlist_browser.py +260 -107
- souleyez/ui.py +464 -312
- souleyez/utils/tool_checker.py +427 -367
- souleyez/utils.py +33 -29
- souleyez/wordlists.py +134 -167
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
- souleyez-3.0.0.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
souleyez/ui/tool_setup.py
CHANGED
|
@@ -29,48 +29,53 @@ def _reset_terminal():
|
|
|
29
29
|
"""Reset terminal to sane state after interrupt."""
|
|
30
30
|
try:
|
|
31
31
|
# Reset terminal using stty
|
|
32
|
-
subprocess.run([
|
|
32
|
+
subprocess.run(["stty", "sane"], check=False, timeout=5)
|
|
33
33
|
# Also try the reset command for good measure
|
|
34
|
-
subprocess.run(
|
|
35
|
-
|
|
34
|
+
subprocess.run(
|
|
35
|
+
["reset", "-I"],
|
|
36
|
+
check=False,
|
|
37
|
+
timeout=5,
|
|
38
|
+
stdout=subprocess.DEVNULL,
|
|
39
|
+
stderr=subprocess.DEVNULL,
|
|
40
|
+
)
|
|
36
41
|
except Exception:
|
|
37
42
|
pass
|
|
38
43
|
|
|
39
44
|
|
|
40
45
|
# Prerequisites needed for various install methods
|
|
41
46
|
PREREQUISITES = {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
"build-deps": {
|
|
48
|
+
"check": None, # Always install to ensure all deps present
|
|
49
|
+
"install": "sudo apt install -y build-essential python3-dev libxml2-dev libxslt1-dev libuv1-dev libffi-dev libssl-dev rustc cargo",
|
|
50
|
+
"description": "Build dependencies for Python packages with native extensions",
|
|
51
|
+
"always_install": True, # Flag to always run this
|
|
47
52
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
"pipx": {
|
|
54
|
+
"check": "pipx",
|
|
55
|
+
"install": "sudo apt install -y pipx && pipx ensurepath",
|
|
56
|
+
"description": "Python application installer (for theHarvester, NetExec, etc.)",
|
|
57
|
+
"path_additions": ["~/.local/bin"],
|
|
53
58
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
"golang": {
|
|
60
|
+
"check": "go",
|
|
61
|
+
"install": "sudo apt install -y golang-go",
|
|
62
|
+
"description": "Go programming language (for nuclei, ffuf)",
|
|
63
|
+
"path_additions": ["~/go/bin"],
|
|
59
64
|
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
"ruby": {
|
|
66
|
+
"check": "gem",
|
|
67
|
+
"install": "sudo apt install -y ruby-full ruby-dev build-essential",
|
|
68
|
+
"description": "Ruby programming language (for wpscan)",
|
|
64
69
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
"snap": {
|
|
71
|
+
"check": "snap",
|
|
72
|
+
"install": "sudo apt install -y snapd",
|
|
73
|
+
"description": "Snap package manager (for enum4linux)",
|
|
69
74
|
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
"git": {
|
|
76
|
+
"check": "git",
|
|
77
|
+
"install": "sudo apt install -y git",
|
|
78
|
+
"description": "Git version control (for exploitdb, Responder)",
|
|
74
79
|
},
|
|
75
80
|
}
|
|
76
81
|
|
|
@@ -129,12 +134,15 @@ def _add_paths_to_shell_rc():
|
|
|
129
134
|
pass # Don't fail on PATH configuration issues
|
|
130
135
|
|
|
131
136
|
|
|
132
|
-
def _run_command(
|
|
137
|
+
def _run_command(
|
|
138
|
+
cmd: str, console, description: str = "", capture: bool = False
|
|
139
|
+
) -> tuple:
|
|
133
140
|
"""Run a command with proper error handling."""
|
|
134
141
|
import sys
|
|
142
|
+
|
|
135
143
|
try:
|
|
136
144
|
# Never capture sudo commands - password prompt needs to be visible
|
|
137
|
-
if cmd.strip().startswith(
|
|
145
|
+
if cmd.strip().startswith("sudo"):
|
|
138
146
|
capture = False
|
|
139
147
|
# Flush output and print newline so sudo prompt appears on new line
|
|
140
148
|
sys.stdout.flush()
|
|
@@ -147,7 +155,7 @@ def _run_command(cmd: str, console, description: str = "", capture: bool = False
|
|
|
147
155
|
shell=True,
|
|
148
156
|
capture_output=True,
|
|
149
157
|
text=True,
|
|
150
|
-
timeout=600 # 10 minute timeout
|
|
158
|
+
timeout=600, # 10 minute timeout
|
|
151
159
|
)
|
|
152
160
|
return result.returncode == 0, result.stdout, result.stderr
|
|
153
161
|
else:
|
|
@@ -155,7 +163,7 @@ def _run_command(cmd: str, console, description: str = "", capture: bool = False
|
|
|
155
163
|
cmd,
|
|
156
164
|
shell=True,
|
|
157
165
|
stdin=sys.stdin, # Ensure stdin is connected for password input
|
|
158
|
-
timeout=600
|
|
166
|
+
timeout=600,
|
|
159
167
|
)
|
|
160
168
|
return result.returncode == 0, "", ""
|
|
161
169
|
except subprocess.TimeoutExpired:
|
|
@@ -166,19 +174,19 @@ def _run_command(cmd: str, console, description: str = "", capture: bool = False
|
|
|
166
174
|
|
|
167
175
|
# Tools that need privileged access
|
|
168
176
|
# Binary tools: simple executables that need sudo
|
|
169
|
-
PRIVILEGED_BINARY_TOOLS = [
|
|
177
|
+
PRIVILEGED_BINARY_TOOLS = ["nmap"]
|
|
170
178
|
|
|
171
179
|
# Script-based tools: need sudo to run interpreter + script
|
|
172
180
|
# Format: {'name': {'interpreter': path, 'script_paths': [possible locations], 'description': str}}
|
|
173
181
|
PRIVILEGED_SCRIPT_TOOLS = {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
str(Path.home() /
|
|
182
|
+
"responder": {
|
|
183
|
+
"interpreter": "/usr/bin/python3",
|
|
184
|
+
"script_paths": [
|
|
185
|
+
"/usr/share/responder/Responder.py",
|
|
186
|
+
"/opt/Responder/Responder.py",
|
|
187
|
+
str(Path.home() / "tools/Responder/Responder.py"),
|
|
180
188
|
],
|
|
181
|
-
|
|
189
|
+
"description": "LLMNR/NBT-NS credential capture",
|
|
182
190
|
}
|
|
183
191
|
}
|
|
184
192
|
|
|
@@ -199,11 +207,9 @@ def _check_sudoers_configured_binary(tool_name: str) -> bool:
|
|
|
199
207
|
|
|
200
208
|
# Try running sudo -n (non-interactive) to see if NOPASSWD is set
|
|
201
209
|
try:
|
|
202
|
-
subprocess.run([
|
|
210
|
+
subprocess.run(["sudo", "-k"], capture_output=True, timeout=5)
|
|
203
211
|
result = subprocess.run(
|
|
204
|
-
[
|
|
205
|
-
capture_output=True,
|
|
206
|
-
timeout=5
|
|
212
|
+
["sudo", "-n", tool_path, "--version"], capture_output=True, timeout=5
|
|
207
213
|
)
|
|
208
214
|
return result.returncode == 0
|
|
209
215
|
except Exception:
|
|
@@ -217,11 +223,11 @@ def _check_sudoers_configured_script(interpreter: str, script_path: str) -> bool
|
|
|
217
223
|
|
|
218
224
|
# Try running sudo -n with interpreter + script
|
|
219
225
|
try:
|
|
220
|
-
subprocess.run([
|
|
226
|
+
subprocess.run(["sudo", "-k"], capture_output=True, timeout=5)
|
|
221
227
|
result = subprocess.run(
|
|
222
|
-
[
|
|
228
|
+
["sudo", "-n", interpreter, script_path, "--help"],
|
|
223
229
|
capture_output=True,
|
|
224
|
-
timeout=5
|
|
230
|
+
timeout=5,
|
|
225
231
|
)
|
|
226
232
|
return result.returncode == 0
|
|
227
233
|
except Exception:
|
|
@@ -243,11 +249,13 @@ def _configure_sudoers(console):
|
|
|
243
249
|
|
|
244
250
|
# Check script-based tools
|
|
245
251
|
for tool_name, tool_info in PRIVILEGED_SCRIPT_TOOLS.items():
|
|
246
|
-
script_path = _find_script_path(tool_info[
|
|
252
|
+
script_path = _find_script_path(tool_info["script_paths"])
|
|
247
253
|
if script_path:
|
|
248
|
-
interpreter = tool_info[
|
|
254
|
+
interpreter = tool_info["interpreter"]
|
|
249
255
|
if not _check_sudoers_configured_script(interpreter, script_path):
|
|
250
|
-
script_tools_to_configure.append(
|
|
256
|
+
script_tools_to_configure.append(
|
|
257
|
+
(tool_name, interpreter, script_path, tool_info["description"])
|
|
258
|
+
)
|
|
251
259
|
|
|
252
260
|
if not binary_tools_to_configure and not script_tools_to_configure:
|
|
253
261
|
return # All configured or not installed
|
|
@@ -256,7 +264,9 @@ def _configure_sudoers(console):
|
|
|
256
264
|
console.print(" " + "═" * 60)
|
|
257
265
|
console.print("[bold cyan] PRIVILEGED SCAN SETUP[/bold cyan]")
|
|
258
266
|
console.print()
|
|
259
|
-
console.print(
|
|
267
|
+
console.print(
|
|
268
|
+
" Some scans require root privileges (SYN scans, credential capture)."
|
|
269
|
+
)
|
|
260
270
|
console.print(" SoulEyez can configure passwordless sudo so these scans")
|
|
261
271
|
console.print(" work automatically without running as root.")
|
|
262
272
|
console.print()
|
|
@@ -267,9 +277,13 @@ def _configure_sudoers(console):
|
|
|
267
277
|
console.print(f" • {tool_name} ({description})")
|
|
268
278
|
console.print()
|
|
269
279
|
|
|
270
|
-
if not click.confirm(
|
|
280
|
+
if not click.confirm(
|
|
281
|
+
" Configure passwordless sudo for these tools?", default=True
|
|
282
|
+
):
|
|
271
283
|
console.print()
|
|
272
|
-
console.print(
|
|
284
|
+
console.print(
|
|
285
|
+
" [yellow]Skipped.[/yellow] Privileged scans will require running as root."
|
|
286
|
+
)
|
|
273
287
|
console.print(" Run 'souleyez setup --fix-permissions' later to configure.")
|
|
274
288
|
return
|
|
275
289
|
|
|
@@ -287,7 +301,9 @@ def _configure_sudoers(console):
|
|
|
287
301
|
proc = subprocess.run(cmd, shell=True, timeout=60) # nosec B602
|
|
288
302
|
|
|
289
303
|
if proc.returncode == 0:
|
|
290
|
-
console.print(
|
|
304
|
+
console.print(
|
|
305
|
+
f" [green]✓[/green] {tool_name} - configured for privileged scans"
|
|
306
|
+
)
|
|
291
307
|
else:
|
|
292
308
|
console.print(f" [red]✗[/red] {tool_name} - failed to configure")
|
|
293
309
|
except subprocess.TimeoutExpired:
|
|
@@ -306,7 +322,9 @@ def _configure_sudoers(console):
|
|
|
306
322
|
proc = subprocess.run(cmd, shell=True, timeout=60) # nosec B602
|
|
307
323
|
|
|
308
324
|
if proc.returncode == 0:
|
|
309
|
-
console.print(
|
|
325
|
+
console.print(
|
|
326
|
+
f" [green]✓[/green] {tool_name} - configured for privileged scans"
|
|
327
|
+
)
|
|
310
328
|
else:
|
|
311
329
|
console.print(f" [red]✗[/red] {tool_name} - failed to configure")
|
|
312
330
|
except subprocess.TimeoutExpired:
|
|
@@ -322,15 +340,15 @@ def _ensure_msfdb_initialized(console):
|
|
|
322
340
|
from pathlib import Path
|
|
323
341
|
|
|
324
342
|
# Check if msfdb exists (either apt install or official installer)
|
|
325
|
-
msfdb_path = shutil.which(
|
|
343
|
+
msfdb_path = shutil.which("msfdb")
|
|
326
344
|
if not msfdb_path:
|
|
327
345
|
# Try the official installer path
|
|
328
|
-
msfdb_path =
|
|
346
|
+
msfdb_path = "/opt/metasploit-framework/bin/msfdb"
|
|
329
347
|
if not Path(msfdb_path).exists():
|
|
330
348
|
return # Metasploit not installed
|
|
331
349
|
|
|
332
350
|
# Check if database is already initialized by looking for database.yml
|
|
333
|
-
user_db_yml = Path.home() /
|
|
351
|
+
user_db_yml = Path.home() / ".msf4" / "database.yml"
|
|
334
352
|
if user_db_yml.exists():
|
|
335
353
|
return # Already initialized
|
|
336
354
|
|
|
@@ -344,7 +362,9 @@ def _ensure_msfdb_initialized(console):
|
|
|
344
362
|
|
|
345
363
|
if not click.confirm(" Initialize MSF database now?", default=True):
|
|
346
364
|
console.print()
|
|
347
|
-
console.print(
|
|
365
|
+
console.print(
|
|
366
|
+
" [yellow]Skipped.[/yellow] Run 'sudo msfdb init' manually when needed."
|
|
367
|
+
)
|
|
348
368
|
return
|
|
349
369
|
|
|
350
370
|
console.print()
|
|
@@ -353,30 +373,36 @@ def _ensure_msfdb_initialized(console):
|
|
|
353
373
|
try:
|
|
354
374
|
# Use sudo for msfdb init (required on Kali and some other distros)
|
|
355
375
|
result = subprocess.run(
|
|
356
|
-
[
|
|
376
|
+
["sudo", msfdb_path, "init"],
|
|
357
377
|
capture_output=True,
|
|
358
|
-
timeout=300 # 5 minute timeout
|
|
378
|
+
timeout=300, # 5 minute timeout
|
|
359
379
|
)
|
|
360
380
|
|
|
361
381
|
if result.returncode == 0:
|
|
362
382
|
console.print(" [green]✓ MSF database initialized[/green]")
|
|
363
383
|
|
|
364
384
|
# Copy database.yml to /root/.msf4/ so sudo msfconsole can connect
|
|
365
|
-
user_db_yml = Path.home() /
|
|
385
|
+
user_db_yml = Path.home() / ".msf4" / "database.yml"
|
|
366
386
|
if user_db_yml.exists():
|
|
367
387
|
console.print(" [dim]Configuring sudo access to MSF database...[/dim]")
|
|
368
388
|
try:
|
|
369
|
-
subprocess.run(
|
|
389
|
+
subprocess.run(
|
|
390
|
+
["sudo", "mkdir", "-p", "/root/.msf4"], capture_output=True
|
|
391
|
+
)
|
|
370
392
|
copy_result = subprocess.run(
|
|
371
|
-
[
|
|
372
|
-
capture_output=True
|
|
393
|
+
["sudo", "cp", str(user_db_yml), "/root/.msf4/database.yml"],
|
|
394
|
+
capture_output=True,
|
|
373
395
|
)
|
|
374
396
|
if copy_result.returncode == 0:
|
|
375
397
|
console.print(" [green]✓ Sudo MSF access configured[/green]")
|
|
376
398
|
else:
|
|
377
|
-
console.print(
|
|
399
|
+
console.print(
|
|
400
|
+
" [yellow]⚠ Run: sudo cp ~/.msf4/database.yml /root/.msf4/[/yellow]"
|
|
401
|
+
)
|
|
378
402
|
except Exception:
|
|
379
|
-
console.print(
|
|
403
|
+
console.print(
|
|
404
|
+
" [yellow]⚠ Run: sudo cp ~/.msf4/database.yml /root/.msf4/[/yellow]"
|
|
405
|
+
)
|
|
380
406
|
else:
|
|
381
407
|
stderr = result.stderr.decode() if result.stderr else ""
|
|
382
408
|
if "already" in stderr.lower():
|
|
@@ -386,7 +412,9 @@ def _ensure_msfdb_initialized(console):
|
|
|
386
412
|
except subprocess.TimeoutExpired:
|
|
387
413
|
console.print(" [yellow]⚠ msfdb init timed out (run manually)[/yellow]")
|
|
388
414
|
except Exception as e:
|
|
389
|
-
console.print(
|
|
415
|
+
console.print(
|
|
416
|
+
f" [yellow]⚠ Could not init MSF database: {str(e)[:40]}[/yellow]"
|
|
417
|
+
)
|
|
390
418
|
|
|
391
419
|
console.print()
|
|
392
420
|
|
|
@@ -413,30 +441,42 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
413
441
|
# Header
|
|
414
442
|
DesignSystem.clear_screen()
|
|
415
443
|
console.print()
|
|
416
|
-
console.print(
|
|
417
|
-
|
|
418
|
-
|
|
444
|
+
console.print(
|
|
445
|
+
"[bold cyan]╔══════════════════════════════════════════════════════════════╗[/bold cyan]"
|
|
446
|
+
)
|
|
447
|
+
console.print(
|
|
448
|
+
"[bold cyan]║ SOULEYEZ TOOL SETUP WIZARD ║[/bold cyan]"
|
|
449
|
+
)
|
|
450
|
+
console.print(
|
|
451
|
+
"[bold cyan]╚══════════════════════════════════════════════════════════════╝[/bold cyan]"
|
|
452
|
+
)
|
|
419
453
|
console.print()
|
|
420
454
|
|
|
421
455
|
# Distro detection
|
|
422
456
|
distro_names = {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
457
|
+
"kali": "Kali Linux",
|
|
458
|
+
"parrot": "Parrot OS",
|
|
459
|
+
"ubuntu": "Ubuntu",
|
|
460
|
+
"debian": "Debian",
|
|
461
|
+
"unknown": "Unknown Linux",
|
|
428
462
|
}
|
|
429
463
|
console.print(f" Detected OS: [bold]{distro_names.get(distro, distro)}[/bold]")
|
|
430
464
|
console.print()
|
|
431
465
|
|
|
432
466
|
# Show distro-specific messaging
|
|
433
|
-
if distro in (
|
|
467
|
+
if distro in ("kali", "parrot"):
|
|
434
468
|
console.print(" [green]✓ You're on a pentesting distro![/green]")
|
|
435
|
-
console.print(
|
|
469
|
+
console.print(
|
|
470
|
+
" Most tools are available via apt. Some may use pipx or direct download."
|
|
471
|
+
)
|
|
436
472
|
console.print()
|
|
437
|
-
elif distro in (
|
|
438
|
-
console.print(
|
|
439
|
-
|
|
473
|
+
elif distro in ("ubuntu", "debian"):
|
|
474
|
+
console.print(
|
|
475
|
+
" [yellow]Note:[/yellow] Some pentesting tools aren't in Ubuntu/Debian repos."
|
|
476
|
+
)
|
|
477
|
+
console.print(
|
|
478
|
+
" This wizard will install them using pipx, go, snap, or from source."
|
|
479
|
+
)
|
|
440
480
|
console.print()
|
|
441
481
|
|
|
442
482
|
_show_tool_status(console)
|
|
@@ -465,13 +505,18 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
465
505
|
_check_prerequisites(console, missing, distro)
|
|
466
506
|
|
|
467
507
|
# Group tools by install method for smarter ordering
|
|
468
|
-
apt_tools = [t for t in missing if t[
|
|
469
|
-
pipx_tools = [t for t in missing if
|
|
470
|
-
go_tools = [t for t in missing if
|
|
471
|
-
gem_tools = [t for t in missing if
|
|
472
|
-
snap_tools = [t for t in missing if
|
|
473
|
-
git_tools = [t for t in missing if
|
|
474
|
-
other_tools = [
|
|
508
|
+
apt_tools = [t for t in missing if t["install_method"] == "apt"]
|
|
509
|
+
pipx_tools = [t for t in missing if "pipx" in t["install"]]
|
|
510
|
+
go_tools = [t for t in missing if "go install" in t["install"]]
|
|
511
|
+
gem_tools = [t for t in missing if "gem install" in t["install"]]
|
|
512
|
+
snap_tools = [t for t in missing if "snap install" in t["install"]]
|
|
513
|
+
git_tools = [t for t in missing if "git clone" in t["install"]]
|
|
514
|
+
other_tools = [
|
|
515
|
+
t
|
|
516
|
+
for t in missing
|
|
517
|
+
if t
|
|
518
|
+
not in apt_tools + pipx_tools + go_tools + gem_tools + snap_tools + git_tools
|
|
519
|
+
]
|
|
475
520
|
|
|
476
521
|
total_to_install = len(missing)
|
|
477
522
|
installed_count = 0
|
|
@@ -485,7 +530,7 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
485
530
|
if success:
|
|
486
531
|
installed_count += len(apt_tools)
|
|
487
532
|
else:
|
|
488
|
-
failed_tools.extend([t[
|
|
533
|
+
failed_tools.extend([t["name"] for t in apt_tools])
|
|
489
534
|
|
|
490
535
|
# Install pipx tools
|
|
491
536
|
if pipx_tools:
|
|
@@ -495,7 +540,7 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
495
540
|
if _install_pipx_tool(console, tool):
|
|
496
541
|
installed_count += 1
|
|
497
542
|
else:
|
|
498
|
-
failed_tools.append(tool[
|
|
543
|
+
failed_tools.append(tool["name"])
|
|
499
544
|
|
|
500
545
|
# Install go tools
|
|
501
546
|
if go_tools:
|
|
@@ -505,7 +550,7 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
505
550
|
if _install_go_tool(console, tool):
|
|
506
551
|
installed_count += 1
|
|
507
552
|
else:
|
|
508
|
-
failed_tools.append(tool[
|
|
553
|
+
failed_tools.append(tool["name"])
|
|
509
554
|
|
|
510
555
|
# Install gem tools
|
|
511
556
|
if gem_tools:
|
|
@@ -515,7 +560,7 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
515
560
|
if _install_gem_tool(console, tool):
|
|
516
561
|
installed_count += 1
|
|
517
562
|
else:
|
|
518
|
-
failed_tools.append(tool[
|
|
563
|
+
failed_tools.append(tool["name"])
|
|
519
564
|
|
|
520
565
|
# Install snap tools
|
|
521
566
|
if snap_tools:
|
|
@@ -525,7 +570,7 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
525
570
|
if _install_snap_tool(console, tool):
|
|
526
571
|
installed_count += 1
|
|
527
572
|
else:
|
|
528
|
-
failed_tools.append(tool[
|
|
573
|
+
failed_tools.append(tool["name"])
|
|
529
574
|
|
|
530
575
|
# Install git-based tools
|
|
531
576
|
if git_tools:
|
|
@@ -535,7 +580,7 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
535
580
|
if _install_git_tool(console, tool):
|
|
536
581
|
installed_count += 1
|
|
537
582
|
else:
|
|
538
|
-
failed_tools.append(tool[
|
|
583
|
+
failed_tools.append(tool["name"])
|
|
539
584
|
|
|
540
585
|
# Install other tools (like metasploit)
|
|
541
586
|
if other_tools:
|
|
@@ -545,7 +590,7 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
545
590
|
if _install_other_tool(console, tool):
|
|
546
591
|
installed_count += 1
|
|
547
592
|
else:
|
|
548
|
-
failed_tools.append(tool[
|
|
593
|
+
failed_tools.append(tool["name"])
|
|
549
594
|
|
|
550
595
|
# Configure PATH in shell rc files (bash and zsh)
|
|
551
596
|
_add_paths_to_shell_rc()
|
|
@@ -557,7 +602,9 @@ def _run_tool_setup_impl(check_only: bool = False, install_all: bool = False):
|
|
|
557
602
|
console.print()
|
|
558
603
|
|
|
559
604
|
if failed_tools:
|
|
560
|
-
console.print(
|
|
605
|
+
console.print(
|
|
606
|
+
f" [yellow]⚠ {len(failed_tools)} tools failed to install:[/yellow]"
|
|
607
|
+
)
|
|
561
608
|
for name in failed_tools:
|
|
562
609
|
console.print(f" • {name}")
|
|
563
610
|
console.print()
|
|
@@ -585,7 +632,7 @@ def _run_post_install_tasks(console, distro: str):
|
|
|
585
632
|
console.print()
|
|
586
633
|
console.print(" [yellow]Important:[/yellow] To use newly installed tools, either:")
|
|
587
634
|
console.print(" 1. Restart your terminal, OR")
|
|
588
|
-
if distro in (
|
|
635
|
+
if distro in ("kali", "parrot"):
|
|
589
636
|
console.print(" 2. Run: [cyan]source ~/.zshrc[/cyan] (Kali uses zsh)")
|
|
590
637
|
else:
|
|
591
638
|
console.print(" 2. Run: [cyan]source ~/.bashrc[/cyan]")
|
|
@@ -597,8 +644,12 @@ def _show_tool_status(console):
|
|
|
597
644
|
tools_by_cat = get_tools_by_category()
|
|
598
645
|
installed, total = get_tool_stats()
|
|
599
646
|
|
|
600
|
-
status_color =
|
|
601
|
-
|
|
647
|
+
status_color = (
|
|
648
|
+
"green" if installed == total else "yellow" if installed > 0 else "red"
|
|
649
|
+
)
|
|
650
|
+
console.print(
|
|
651
|
+
f" [bold]Tool Status:[/bold] [{status_color}]{installed}/{total} installed[/{status_color}]"
|
|
652
|
+
)
|
|
602
653
|
console.print()
|
|
603
654
|
|
|
604
655
|
for category, tools in tools_by_cat.items():
|
|
@@ -606,12 +657,14 @@ def _show_tool_status(console):
|
|
|
606
657
|
console.print(f" [bold]{cat_name}[/bold]")
|
|
607
658
|
|
|
608
659
|
for tool in tools:
|
|
609
|
-
if tool[
|
|
660
|
+
if tool["installed"]:
|
|
610
661
|
status = "[green]✓[/green]"
|
|
611
662
|
else:
|
|
612
663
|
status = "[red]✗[/red]"
|
|
613
664
|
|
|
614
|
-
console.print(
|
|
665
|
+
console.print(
|
|
666
|
+
f" {status} {tool['name']:<18} - {tool['description'][:40]}"
|
|
667
|
+
)
|
|
615
668
|
|
|
616
669
|
console.print()
|
|
617
670
|
|
|
@@ -621,32 +674,41 @@ def _check_prerequisites(console, missing_tools: List[Dict], distro: str):
|
|
|
621
674
|
needed_prereqs = set()
|
|
622
675
|
|
|
623
676
|
for tool in missing_tools:
|
|
624
|
-
install_cmd = tool[
|
|
625
|
-
if
|
|
626
|
-
needed_prereqs.add(
|
|
677
|
+
install_cmd = tool["install"]
|
|
678
|
+
if "pipx" in install_cmd:
|
|
679
|
+
needed_prereqs.add("pipx")
|
|
627
680
|
# pipx tools with native extensions need build dependencies
|
|
628
|
-
needed_prereqs.add(
|
|
629
|
-
if
|
|
630
|
-
needed_prereqs.add(
|
|
631
|
-
if
|
|
632
|
-
needed_prereqs.add(
|
|
633
|
-
if
|
|
634
|
-
needed_prereqs.add(
|
|
635
|
-
if
|
|
636
|
-
needed_prereqs.add(
|
|
681
|
+
needed_prereqs.add("build-deps")
|
|
682
|
+
if "go install" in install_cmd:
|
|
683
|
+
needed_prereqs.add("golang")
|
|
684
|
+
if "gem install" in install_cmd:
|
|
685
|
+
needed_prereqs.add("ruby")
|
|
686
|
+
if "snap install" in install_cmd:
|
|
687
|
+
needed_prereqs.add("snap")
|
|
688
|
+
if "git clone" in install_cmd:
|
|
689
|
+
needed_prereqs.add("git")
|
|
637
690
|
|
|
638
691
|
missing_prereqs = []
|
|
639
692
|
for prereq in needed_prereqs:
|
|
640
693
|
info = PREREQUISITES[prereq]
|
|
641
694
|
# Check if always_install flag is set, or if tool check fails
|
|
642
|
-
if info.get(
|
|
695
|
+
if info.get("always_install") or (
|
|
696
|
+
info.get("check") and not check_tool(info["check"])
|
|
697
|
+
):
|
|
643
698
|
missing_prereqs.append((prereq, info))
|
|
644
699
|
|
|
645
700
|
if not missing_prereqs:
|
|
646
701
|
return
|
|
647
702
|
|
|
648
703
|
# Sort to ensure build-deps comes first (needed before pipx installs)
|
|
649
|
-
prereq_order = {
|
|
704
|
+
prereq_order = {
|
|
705
|
+
"build-deps": 0,
|
|
706
|
+
"pipx": 1,
|
|
707
|
+
"golang": 2,
|
|
708
|
+
"ruby": 3,
|
|
709
|
+
"snap": 4,
|
|
710
|
+
"git": 5,
|
|
711
|
+
}
|
|
650
712
|
missing_prereqs.sort(key=lambda x: prereq_order.get(x[0], 99))
|
|
651
713
|
|
|
652
714
|
console.print()
|
|
@@ -654,12 +716,12 @@ def _check_prerequisites(console, missing_tools: List[Dict], distro: str):
|
|
|
654
716
|
|
|
655
717
|
for prereq, info in missing_prereqs:
|
|
656
718
|
console.print(f" Installing {prereq}...", end=" ")
|
|
657
|
-
success, _, stderr = _run_command(info[
|
|
719
|
+
success, _, stderr = _run_command(info["install"], console, capture=True)
|
|
658
720
|
if success:
|
|
659
721
|
console.print("[green]✓[/green]")
|
|
660
722
|
|
|
661
723
|
# Run pipx ensurepath if we just installed pipx
|
|
662
|
-
if prereq ==
|
|
724
|
+
if prereq == "pipx":
|
|
663
725
|
subprocess.run("pipx ensurepath", shell=True, capture_output=True)
|
|
664
726
|
else:
|
|
665
727
|
console.print(f"[red]✗[/red] {stderr[:50]}")
|
|
@@ -672,14 +734,14 @@ def _install_apt_tools(console, tools: List[Dict]) -> bool:
|
|
|
672
734
|
"""Install tools available via apt."""
|
|
673
735
|
packages = []
|
|
674
736
|
for tool in tools:
|
|
675
|
-
cmd = tool[
|
|
676
|
-
if
|
|
737
|
+
cmd = tool["install"]
|
|
738
|
+
if "apt install" in cmd:
|
|
677
739
|
# Extract package name
|
|
678
740
|
parts = cmd.split()
|
|
679
741
|
for i, part in enumerate(parts):
|
|
680
|
-
if part ==
|
|
742
|
+
if part == "install" and i + 1 < len(parts):
|
|
681
743
|
pkg = parts[i + 1]
|
|
682
|
-
if not pkg.startswith(
|
|
744
|
+
if not pkg.startswith("-"):
|
|
683
745
|
packages.append(pkg)
|
|
684
746
|
break
|
|
685
747
|
|
|
@@ -700,8 +762,8 @@ def _install_apt_tools(console, tools: List[Dict]) -> bool:
|
|
|
700
762
|
|
|
701
763
|
def _install_pipx_tool(console, tool: Dict) -> bool:
|
|
702
764
|
"""Install a tool using pipx."""
|
|
703
|
-
name = tool[
|
|
704
|
-
cmd = tool[
|
|
765
|
+
name = tool["name"]
|
|
766
|
+
cmd = tool["install"]
|
|
705
767
|
|
|
706
768
|
console.print(f" {name}...", end=" ")
|
|
707
769
|
|
|
@@ -724,8 +786,8 @@ def _install_pipx_tool(console, tool: Dict) -> bool:
|
|
|
724
786
|
|
|
725
787
|
def _install_go_tool(console, tool: Dict) -> bool:
|
|
726
788
|
"""Install a tool using go install."""
|
|
727
|
-
name = tool[
|
|
728
|
-
cmd = tool[
|
|
789
|
+
name = tool["name"]
|
|
790
|
+
cmd = tool["install"]
|
|
729
791
|
|
|
730
792
|
console.print(f" {name}...", end=" ")
|
|
731
793
|
|
|
@@ -742,7 +804,7 @@ def _install_go_tool(console, tool: Dict) -> bool:
|
|
|
742
804
|
capture_output=True,
|
|
743
805
|
text=True,
|
|
744
806
|
env=env,
|
|
745
|
-
timeout=300 # 5 minute timeout for go installs
|
|
807
|
+
timeout=300, # 5 minute timeout for go installs
|
|
746
808
|
)
|
|
747
809
|
if result.returncode == 0:
|
|
748
810
|
console.print("[green]✓[/green]")
|
|
@@ -759,8 +821,8 @@ def _install_go_tool(console, tool: Dict) -> bool:
|
|
|
759
821
|
|
|
760
822
|
def _install_gem_tool(console, tool: Dict) -> bool:
|
|
761
823
|
"""Install a tool using gem."""
|
|
762
|
-
name = tool[
|
|
763
|
-
cmd = tool[
|
|
824
|
+
name = tool["name"]
|
|
825
|
+
cmd = tool["install"]
|
|
764
826
|
|
|
765
827
|
console.print(f" {name}...", end=" ")
|
|
766
828
|
|
|
@@ -778,8 +840,8 @@ def _install_gem_tool(console, tool: Dict) -> bool:
|
|
|
778
840
|
|
|
779
841
|
def _install_snap_tool(console, tool: Dict) -> bool:
|
|
780
842
|
"""Install a tool using snap."""
|
|
781
|
-
name = tool[
|
|
782
|
-
cmd = tool[
|
|
843
|
+
name = tool["name"]
|
|
844
|
+
cmd = tool["install"]
|
|
783
845
|
|
|
784
846
|
console.print(f" {name}...", end=" ")
|
|
785
847
|
|
|
@@ -799,36 +861,52 @@ def _install_git_tool(console, tool: Dict) -> bool:
|
|
|
799
861
|
"""Install a tool from git (requires sudo for /opt)."""
|
|
800
862
|
import re
|
|
801
863
|
|
|
802
|
-
name = tool[
|
|
803
|
-
cmd = tool[
|
|
864
|
+
name = tool["name"]
|
|
865
|
+
cmd = tool["install"]
|
|
804
866
|
|
|
805
867
|
console.print(f" {name}...")
|
|
806
868
|
|
|
807
869
|
# Parse the command to handle existing directories properly
|
|
808
|
-
# Commands are typically: git clone <url> <dir> && pip install ... && ln -sf ...
|
|
809
|
-
commands = [c.strip() for c in cmd.split(
|
|
870
|
+
# Commands are typically: prereq && git clone <url> <dir> && pip install ... && ln -sf ...
|
|
871
|
+
commands = [c.strip() for c in cmd.split("&&")]
|
|
810
872
|
|
|
811
873
|
clone_cmd = None
|
|
874
|
+
pre_clone_cmds = []
|
|
812
875
|
post_clone_cmds = []
|
|
813
876
|
target_dir = None
|
|
814
877
|
|
|
815
878
|
for i, c in enumerate(commands):
|
|
816
|
-
if
|
|
879
|
+
if "git clone" in c:
|
|
817
880
|
clone_cmd = c
|
|
818
|
-
|
|
881
|
+
pre_clone_cmds = commands[:i] # Commands before git clone (e.g., cpan)
|
|
882
|
+
post_clone_cmds = commands[i + 1 :]
|
|
819
883
|
# Extract target directory from clone command
|
|
820
884
|
# Pattern: git clone <url> <directory>
|
|
821
|
-
match = re.search(r
|
|
885
|
+
match = re.search(r"git clone\s+\S+\s+(\S+)", c)
|
|
822
886
|
if match:
|
|
823
887
|
target_dir = match.group(1)
|
|
824
888
|
break
|
|
825
889
|
|
|
826
890
|
# If we found a git clone command and target directory
|
|
827
891
|
if clone_cmd and target_dir:
|
|
892
|
+
# Run pre-clone commands first (e.g., installing dependencies like cpan)
|
|
893
|
+
for pre_cmd in pre_clone_cmds:
|
|
894
|
+
pre_cmd = pre_cmd.strip()
|
|
895
|
+
if not pre_cmd:
|
|
896
|
+
continue
|
|
897
|
+
console.print(f" [dim]Running: {pre_cmd[:50]}...[/dim]")
|
|
898
|
+
success, _, stderr = _run_command(pre_cmd, console, capture=True)
|
|
899
|
+
if not success:
|
|
900
|
+
console.print(f"[red]✗[/red]")
|
|
901
|
+
if stderr:
|
|
902
|
+
console.print(f" [dim]{stderr[:80]}[/dim]")
|
|
903
|
+
return False
|
|
828
904
|
dir_exists = Path(target_dir).exists()
|
|
829
905
|
|
|
830
906
|
if dir_exists:
|
|
831
|
-
console.print(
|
|
907
|
+
console.print(
|
|
908
|
+
f" [dim]Directory {target_dir} exists, updating...[/dim]"
|
|
909
|
+
)
|
|
832
910
|
# Try to update with git pull
|
|
833
911
|
pull_cmd = f"sudo git -C {target_dir} pull"
|
|
834
912
|
success, _, stderr = _run_command(pull_cmd, console, capture=True)
|
|
@@ -846,7 +924,9 @@ def _install_git_tool(console, tool: Dict) -> bool:
|
|
|
846
924
|
return False
|
|
847
925
|
elif not success:
|
|
848
926
|
# Pull failed for other reasons, try to continue anyway
|
|
849
|
-
console.print(
|
|
927
|
+
console.print(
|
|
928
|
+
f" [yellow]⚠ git pull failed, continuing with existing files[/yellow]"
|
|
929
|
+
)
|
|
850
930
|
else:
|
|
851
931
|
# Directory doesn't exist, run the clone
|
|
852
932
|
success, _, stderr = _run_command(clone_cmd, console, capture=True)
|
|
@@ -886,23 +966,25 @@ def _install_git_tool(console, tool: Dict) -> bool:
|
|
|
886
966
|
|
|
887
967
|
def _install_other_tool(console, tool: Dict) -> bool:
|
|
888
968
|
"""Install tools with custom installation methods (like metasploit)."""
|
|
889
|
-
name = tool[
|
|
890
|
-
cmd = tool[
|
|
969
|
+
name = tool["name"]
|
|
970
|
+
cmd = tool["install"]
|
|
891
971
|
|
|
892
972
|
console.print(f" {name}...", end=" ")
|
|
893
973
|
|
|
894
974
|
# Special handling for metasploit installer (takes a while)
|
|
895
|
-
if
|
|
975
|
+
if "msfinstall" in cmd:
|
|
896
976
|
console.print()
|
|
897
|
-
console.print(
|
|
898
|
-
|
|
977
|
+
console.print(
|
|
978
|
+
" [yellow]Installing Metasploit (this may take several minutes)...[/yellow]"
|
|
979
|
+
)
|
|
980
|
+
console.print(
|
|
981
|
+
" [dim]Installing postgresql and downloading Metasploit installer...[/dim]"
|
|
982
|
+
)
|
|
899
983
|
|
|
900
984
|
# Run the full install command (includes postgresql setup)
|
|
901
985
|
try:
|
|
902
986
|
result = subprocess.run(
|
|
903
|
-
cmd,
|
|
904
|
-
shell=True,
|
|
905
|
-
timeout=1800 # 30 minute timeout for metasploit
|
|
987
|
+
cmd, shell=True, timeout=1800 # 30 minute timeout for metasploit
|
|
906
988
|
)
|
|
907
989
|
if result.returncode == 0:
|
|
908
990
|
console.print(" [green]✓ Metasploit installed[/green]")
|
|
@@ -911,11 +993,11 @@ def _install_other_tool(console, tool: Dict) -> bool:
|
|
|
911
993
|
console.print(" [dim]Initializing MSF database...[/dim]")
|
|
912
994
|
try:
|
|
913
995
|
# Use full path since /opt/metasploit-framework/bin isn't in PATH yet
|
|
914
|
-
msfdb_path =
|
|
996
|
+
msfdb_path = "/opt/metasploit-framework/bin/msfdb"
|
|
915
997
|
init_result = subprocess.run(
|
|
916
|
-
[msfdb_path,
|
|
998
|
+
[msfdb_path, "init"],
|
|
917
999
|
capture_output=True,
|
|
918
|
-
timeout=300 # 5 minute timeout
|
|
1000
|
+
timeout=300, # 5 minute timeout
|
|
919
1001
|
)
|
|
920
1002
|
if init_result.returncode == 0:
|
|
921
1003
|
console.print(" [green]✓ MSF database initialized[/green]")
|
|
@@ -924,30 +1006,48 @@ def _install_other_tool(console, tool: Dict) -> bool:
|
|
|
924
1006
|
# msfdb init creates config in ~/.msf4/, but sudo looks in /root/.msf4/
|
|
925
1007
|
import os
|
|
926
1008
|
from pathlib import Path
|
|
927
|
-
|
|
928
|
-
|
|
1009
|
+
|
|
1010
|
+
user_msf4 = Path.home() / ".msf4"
|
|
1011
|
+
user_db_yml = user_msf4 / "database.yml"
|
|
929
1012
|
if user_db_yml.exists():
|
|
930
|
-
console.print(
|
|
1013
|
+
console.print(
|
|
1014
|
+
" [dim]Configuring sudo access to MSF database...[/dim]"
|
|
1015
|
+
)
|
|
931
1016
|
try:
|
|
932
1017
|
# Create /root/.msf4/ directory and copy database.yml
|
|
933
1018
|
copy_result = subprocess.run(
|
|
934
|
-
[
|
|
935
|
-
capture_output=True
|
|
1019
|
+
["sudo", "mkdir", "-p", "/root/.msf4"],
|
|
1020
|
+
capture_output=True,
|
|
936
1021
|
)
|
|
937
1022
|
copy_result = subprocess.run(
|
|
938
|
-
[
|
|
939
|
-
|
|
1023
|
+
[
|
|
1024
|
+
"sudo",
|
|
1025
|
+
"cp",
|
|
1026
|
+
str(user_db_yml),
|
|
1027
|
+
"/root/.msf4/database.yml",
|
|
1028
|
+
],
|
|
1029
|
+
capture_output=True,
|
|
940
1030
|
)
|
|
941
1031
|
if copy_result.returncode == 0:
|
|
942
|
-
console.print(
|
|
1032
|
+
console.print(
|
|
1033
|
+
" [green]✓ Sudo MSF access configured[/green]"
|
|
1034
|
+
)
|
|
943
1035
|
else:
|
|
944
|
-
console.print(
|
|
1036
|
+
console.print(
|
|
1037
|
+
" [yellow]⚠ Could not configure sudo access (run manually: sudo cp ~/.msf4/database.yml /root/.msf4/)[/yellow]"
|
|
1038
|
+
)
|
|
945
1039
|
except Exception as e:
|
|
946
|
-
console.print(
|
|
1040
|
+
console.print(
|
|
1041
|
+
f" [yellow]⚠ Could not configure sudo access: {str(e)[:30]}[/yellow]"
|
|
1042
|
+
)
|
|
947
1043
|
else:
|
|
948
|
-
console.print(
|
|
1044
|
+
console.print(
|
|
1045
|
+
" [yellow]⚠ MSF database init returned non-zero (may already be initialized)[/yellow]"
|
|
1046
|
+
)
|
|
949
1047
|
except Exception as e:
|
|
950
|
-
console.print(
|
|
1048
|
+
console.print(
|
|
1049
|
+
f" [yellow]⚠ Could not init MSF database: {str(e)[:40]}[/yellow]"
|
|
1050
|
+
)
|
|
951
1051
|
|
|
952
1052
|
return True
|
|
953
1053
|
else:
|