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/reporting/metrics.py
CHANGED
|
@@ -9,108 +9,103 @@ from datetime import datetime
|
|
|
9
9
|
|
|
10
10
|
class MetricsCalculator:
|
|
11
11
|
"""Calculate executive dashboard metrics and risk scores."""
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
def __init__(self):
|
|
14
14
|
pass
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
def calculate_risk_score(self, findings_by_severity: Dict) -> int:
|
|
17
17
|
"""
|
|
18
18
|
Calculate overall risk score (0-100).
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
Weighted scoring:
|
|
21
21
|
- Critical: 25 points each
|
|
22
22
|
- High: 10 points each
|
|
23
23
|
- Medium: 3 points each
|
|
24
24
|
- Low: 1 point each
|
|
25
25
|
- Info: 0 points
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
Capped at 100.
|
|
28
28
|
"""
|
|
29
29
|
score = 0
|
|
30
|
-
score += len(findings_by_severity.get(
|
|
31
|
-
score += len(findings_by_severity.get(
|
|
32
|
-
score += len(findings_by_severity.get(
|
|
33
|
-
score += len(findings_by_severity.get(
|
|
34
|
-
|
|
30
|
+
score += len(findings_by_severity.get("critical", [])) * 25
|
|
31
|
+
score += len(findings_by_severity.get("high", [])) * 10
|
|
32
|
+
score += len(findings_by_severity.get("medium", [])) * 3
|
|
33
|
+
score += len(findings_by_severity.get("low", [])) * 1
|
|
34
|
+
|
|
35
35
|
return min(score, 100)
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
def calculate_exploitation_rate(self, attack_surface: Dict) -> float:
|
|
38
38
|
"""Calculate percentage of services successfully exploited."""
|
|
39
|
-
overview = attack_surface.get(
|
|
40
|
-
total_services = overview.get(
|
|
41
|
-
exploited = overview.get(
|
|
42
|
-
|
|
39
|
+
overview = attack_surface.get("overview", {})
|
|
40
|
+
total_services = overview.get("total_services", 0)
|
|
41
|
+
exploited = overview.get("exploited_services", 0)
|
|
42
|
+
|
|
43
43
|
if total_services == 0:
|
|
44
44
|
return 0.0
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
return round((exploited / total_services) * 100, 1)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
def estimate_remediation_timeline(self, findings_by_severity: Dict) -> Dict:
|
|
49
49
|
"""
|
|
50
50
|
Estimate remediation timeline in days.
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
Estimates:
|
|
53
53
|
- Critical: 1 day each
|
|
54
54
|
- High: 0.5 days each
|
|
55
55
|
- Medium: 0.25 days each
|
|
56
56
|
- Low: 0.1 days each
|
|
57
57
|
"""
|
|
58
|
-
critical_days = len(findings_by_severity.get(
|
|
59
|
-
high_days = len(findings_by_severity.get(
|
|
60
|
-
medium_days = len(findings_by_severity.get(
|
|
61
|
-
low_days = len(findings_by_severity.get(
|
|
62
|
-
|
|
58
|
+
critical_days = len(findings_by_severity.get("critical", [])) * 1
|
|
59
|
+
high_days = len(findings_by_severity.get("high", [])) * 0.5
|
|
60
|
+
medium_days = len(findings_by_severity.get("medium", [])) * 0.25
|
|
61
|
+
low_days = len(findings_by_severity.get("low", [])) * 0.1
|
|
62
|
+
|
|
63
63
|
total_days = critical_days + high_days + medium_days + low_days
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
return {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
"total_days": round(total_days, 1),
|
|
67
|
+
"weeks": round(total_days / 5, 1), # Business days
|
|
68
|
+
"critical": round(critical_days, 1),
|
|
69
|
+
"high": round(high_days, 1),
|
|
70
|
+
"medium": round(medium_days, 1),
|
|
71
|
+
"low": round(low_days, 1),
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
def calculate_host_risk_distribution(self, attack_surface: Dict) -> Dict:
|
|
75
75
|
"""Calculate risk distribution across hosts."""
|
|
76
|
-
hosts = attack_surface.get(
|
|
77
|
-
|
|
78
|
-
distribution = {
|
|
79
|
-
|
|
80
|
-
'high': 0,
|
|
81
|
-
'medium': 0,
|
|
82
|
-
'low': 0
|
|
83
|
-
}
|
|
84
|
-
|
|
76
|
+
hosts = attack_surface.get("hosts", [])
|
|
77
|
+
|
|
78
|
+
distribution = {"critical": 0, "high": 0, "medium": 0, "low": 0}
|
|
79
|
+
|
|
85
80
|
for host in hosts:
|
|
86
|
-
critical_findings = host.get(
|
|
87
|
-
total_findings = host.get(
|
|
88
|
-
|
|
81
|
+
critical_findings = host.get("critical_findings", 0)
|
|
82
|
+
total_findings = host.get("findings", 0)
|
|
83
|
+
|
|
89
84
|
if critical_findings > 0:
|
|
90
|
-
distribution[
|
|
85
|
+
distribution["critical"] += 1
|
|
91
86
|
elif total_findings >= 5:
|
|
92
|
-
distribution[
|
|
87
|
+
distribution["high"] += 1
|
|
93
88
|
elif total_findings >= 2:
|
|
94
|
-
distribution[
|
|
89
|
+
distribution["medium"] += 1
|
|
95
90
|
else:
|
|
96
|
-
distribution[
|
|
97
|
-
|
|
91
|
+
distribution["low"] += 1
|
|
92
|
+
|
|
98
93
|
return distribution
|
|
99
|
-
|
|
94
|
+
|
|
100
95
|
def get_dashboard_metrics(self, data: Dict) -> Dict:
|
|
101
96
|
"""
|
|
102
97
|
Generate all executive dashboard metrics.
|
|
103
|
-
|
|
98
|
+
|
|
104
99
|
Returns comprehensive metrics dict for dashboard rendering.
|
|
105
100
|
"""
|
|
106
|
-
findings_by_severity = data[
|
|
107
|
-
attack_surface = data[
|
|
108
|
-
|
|
101
|
+
findings_by_severity = data["findings_by_severity"]
|
|
102
|
+
attack_surface = data["attack_surface"]
|
|
103
|
+
|
|
109
104
|
risk_score = self.calculate_risk_score(findings_by_severity)
|
|
110
105
|
exploitation_rate = self.calculate_exploitation_rate(attack_surface)
|
|
111
106
|
remediation = self.estimate_remediation_timeline(findings_by_severity)
|
|
112
107
|
host_distribution = self.calculate_host_risk_distribution(attack_surface)
|
|
113
|
-
|
|
108
|
+
|
|
114
109
|
# Determine risk level
|
|
115
110
|
if risk_score >= 75:
|
|
116
111
|
risk_level = "CRITICAL"
|
|
@@ -124,29 +119,31 @@ class MetricsCalculator:
|
|
|
124
119
|
else:
|
|
125
120
|
risk_level = "LOW"
|
|
126
121
|
risk_color = "green"
|
|
127
|
-
|
|
122
|
+
|
|
128
123
|
# Total findings
|
|
129
|
-
total_findings = sum(
|
|
130
|
-
|
|
124
|
+
total_findings = sum(
|
|
125
|
+
len(findings) for findings in findings_by_severity.values()
|
|
126
|
+
)
|
|
127
|
+
|
|
131
128
|
# Overview stats
|
|
132
|
-
overview = attack_surface.get(
|
|
133
|
-
|
|
129
|
+
overview = attack_surface.get("overview", {})
|
|
130
|
+
|
|
134
131
|
return {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
132
|
+
"risk_score": risk_score,
|
|
133
|
+
"risk_level": risk_level,
|
|
134
|
+
"risk_color": risk_color,
|
|
135
|
+
"total_findings": total_findings,
|
|
136
|
+
"critical_findings": len(findings_by_severity.get("critical", [])),
|
|
137
|
+
"high_findings": len(findings_by_severity.get("high", [])),
|
|
138
|
+
"medium_findings": len(findings_by_severity.get("medium", [])),
|
|
139
|
+
"low_findings": len(findings_by_severity.get("low", [])),
|
|
140
|
+
"info_findings": len(findings_by_severity.get("info", [])),
|
|
141
|
+
"total_hosts": overview.get("total_hosts", 0),
|
|
142
|
+
"vulnerable_hosts": overview.get("vulnerable_hosts", 0),
|
|
143
|
+
"total_services": overview.get("total_services", 0),
|
|
144
|
+
"exploited_services": overview.get("exploited_services", 0),
|
|
145
|
+
"exploitation_rate": exploitation_rate,
|
|
146
|
+
"remediation_timeline": remediation,
|
|
147
|
+
"host_risk_distribution": host_distribution,
|
|
148
|
+
"credentials_found": len(data.get("credentials", [])),
|
|
152
149
|
}
|
souleyez/scanner.py
CHANGED
|
@@ -11,10 +11,10 @@ logger = get_logger(__name__)
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _make_paths(target, label):
|
|
14
|
-
safe_label =
|
|
14
|
+
safe_label = label.replace(" ", "_") if label else "scan"
|
|
15
15
|
ts = timestamp_str()
|
|
16
16
|
base = f'nmap_{ts}_{safe_label}_{target.replace("/", "_")}'
|
|
17
|
-
return SCANS_DIR / (base +
|
|
17
|
+
return SCANS_DIR / (base + ".log"), SCANS_DIR / (base + ".xml")
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _parse_nmap_xml_summary(xmlpath: Path):
|
|
@@ -23,54 +23,60 @@ def _parse_nmap_xml_summary(xmlpath: Path):
|
|
|
23
23
|
root = tree.getroot()
|
|
24
24
|
except Exception:
|
|
25
25
|
return None
|
|
26
|
-
hosts = root.findall(
|
|
26
|
+
hosts = root.findall("host")
|
|
27
27
|
hosts_total = len(hosts)
|
|
28
28
|
hosts_up = 0
|
|
29
29
|
open_ports = 0
|
|
30
30
|
per_host = []
|
|
31
31
|
for h in hosts:
|
|
32
32
|
addr = None
|
|
33
|
-
a = h.find(
|
|
33
|
+
a = h.find("address")
|
|
34
34
|
if a is not None:
|
|
35
|
-
addr = a.get(
|
|
36
|
-
st = h.find(
|
|
37
|
-
up =
|
|
35
|
+
addr = a.get("addr")
|
|
36
|
+
st = h.find("status")
|
|
37
|
+
up = st is not None and st.get("state") == "up"
|
|
38
38
|
if up:
|
|
39
39
|
hosts_up += 1
|
|
40
|
-
ports = h.find(
|
|
40
|
+
ports = h.find("ports")
|
|
41
41
|
open_count = 0
|
|
42
42
|
if ports is not None:
|
|
43
|
-
for p in ports.findall(
|
|
44
|
-
state = p.find(
|
|
45
|
-
if state is not None and state.get(
|
|
43
|
+
for p in ports.findall("port"):
|
|
44
|
+
state = p.find("state")
|
|
45
|
+
if state is not None and state.get("state") == "open":
|
|
46
46
|
open_count += 1
|
|
47
47
|
open_ports += open_count
|
|
48
|
-
per_host.append({
|
|
49
|
-
return {
|
|
48
|
+
per_host.append({"addr": addr, "up": up, "open": open_count})
|
|
49
|
+
return {
|
|
50
|
+
"hosts_total": hosts_total,
|
|
51
|
+
"hosts_up": hosts_up,
|
|
52
|
+
"open_ports": open_ports,
|
|
53
|
+
"per_host": per_host,
|
|
54
|
+
}
|
|
50
55
|
|
|
51
56
|
|
|
52
57
|
def run_nmap(target, nmap_args, label=None, save_xml=False):
|
|
53
58
|
if not nmap_installed():
|
|
54
|
-
raise EnvironmentError(
|
|
55
|
-
|
|
59
|
+
raise EnvironmentError("nmap is not installed or not on PATH.")
|
|
60
|
+
|
|
56
61
|
# Validate nmap args to prevent command injection
|
|
57
62
|
try:
|
|
58
63
|
validated_args = validate_nmap_args(nmap_args)
|
|
59
64
|
except ValidationError as e:
|
|
60
|
-
logger.error(
|
|
61
|
-
"args": nmap_args,
|
|
62
|
-
|
|
63
|
-
})
|
|
65
|
+
logger.error(
|
|
66
|
+
"Invalid nmap arguments", extra={"args": nmap_args, "error": str(e)}
|
|
67
|
+
)
|
|
64
68
|
raise
|
|
65
|
-
|
|
66
|
-
logpath, xmlpath = _make_paths(target, label or
|
|
67
|
-
cmd = [
|
|
69
|
+
|
|
70
|
+
logpath, xmlpath = _make_paths(target, label or "scan")
|
|
71
|
+
cmd = ["nmap"] + list(validated_args)
|
|
68
72
|
if save_xml:
|
|
69
|
-
cmd += [
|
|
73
|
+
cmd += ["-oX", str(xmlpath)]
|
|
70
74
|
cmd += [target]
|
|
71
|
-
with open(logpath,
|
|
72
|
-
lf.write(f
|
|
73
|
-
proc = subprocess.Popen(
|
|
75
|
+
with open(logpath, "w", encoding="utf-8", errors="replace") as lf:
|
|
76
|
+
lf.write(f"$ {join_cmd(cmd)}\\n\\n")
|
|
77
|
+
proc = subprocess.Popen(
|
|
78
|
+
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True
|
|
79
|
+
)
|
|
74
80
|
try:
|
|
75
81
|
for line in proc.stdout:
|
|
76
82
|
print(line.rstrip())
|
|
@@ -78,11 +84,11 @@ def run_nmap(target, nmap_args, label=None, save_xml=False):
|
|
|
78
84
|
except KeyboardInterrupt:
|
|
79
85
|
proc.terminate()
|
|
80
86
|
proc.wait(timeout=5)
|
|
81
|
-
print(
|
|
82
|
-
lf.write(
|
|
87
|
+
print("\\n[scan cancelled by user]")
|
|
88
|
+
lf.write("\\n[scan cancelled by user]\\n")
|
|
83
89
|
return logpath, -1, (xmlpath if save_xml else None), None
|
|
84
90
|
rc = proc.wait()
|
|
85
|
-
lf.write(f
|
|
91
|
+
lf.write(f"\\n# exit_code: {rc}\\n")
|
|
86
92
|
summary = None
|
|
87
93
|
if save_xml and xmlpath.exists():
|
|
88
94
|
summary = _parse_nmap_xml_summary(xmlpath)
|
souleyez/security/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Security validation, sanitization, and authentication utilities."""
|
|
2
|
+
|
|
2
3
|
import click
|
|
3
4
|
import sys
|
|
4
5
|
import functools
|
|
@@ -16,6 +17,7 @@ def require_login(f):
|
|
|
16
17
|
def my_command():
|
|
17
18
|
...
|
|
18
19
|
"""
|
|
20
|
+
|
|
19
21
|
@functools.wraps(f)
|
|
20
22
|
def wrapper(*args, **kwargs):
|
|
21
23
|
from souleyez.auth import get_current_user, is_logged_in, init_auth
|
|
@@ -24,16 +26,18 @@ def require_login(f):
|
|
|
24
26
|
# Initialize auth if needed
|
|
25
27
|
try:
|
|
26
28
|
from souleyez.auth import get_session_manager
|
|
29
|
+
|
|
27
30
|
get_session_manager()
|
|
28
31
|
except RuntimeError:
|
|
29
32
|
init_auth(get_db().db_path)
|
|
30
33
|
|
|
31
34
|
if not is_logged_in():
|
|
32
|
-
click.echo(click.style("\n⚠️ Authentication required.", fg=
|
|
33
|
-
click.echo(click.style(" Run: souleyez login", fg=
|
|
35
|
+
click.echo(click.style("\n⚠️ Authentication required.", fg="yellow"))
|
|
36
|
+
click.echo(click.style(" Run: souleyez login", fg="yellow"))
|
|
34
37
|
sys.exit(1)
|
|
35
38
|
|
|
36
39
|
return f(*args, **kwargs)
|
|
40
|
+
|
|
37
41
|
return wrapper
|
|
38
42
|
|
|
39
43
|
|
|
@@ -49,16 +53,18 @@ def require_admin(f):
|
|
|
49
53
|
def admin_only_command():
|
|
50
54
|
...
|
|
51
55
|
"""
|
|
56
|
+
|
|
52
57
|
@functools.wraps(f)
|
|
53
58
|
def wrapper(*args, **kwargs):
|
|
54
59
|
from souleyez.auth import get_current_user, Role
|
|
55
60
|
|
|
56
61
|
user = get_current_user()
|
|
57
62
|
if user is None or user.role != Role.ADMIN:
|
|
58
|
-
click.echo(click.style("\n⚠️ Admin privileges required.", fg=
|
|
63
|
+
click.echo(click.style("\n⚠️ Admin privileges required.", fg="red"))
|
|
59
64
|
sys.exit(1)
|
|
60
65
|
|
|
61
66
|
return f(*args, **kwargs)
|
|
67
|
+
|
|
62
68
|
return wrapper
|
|
63
69
|
|
|
64
70
|
|
|
@@ -74,17 +80,21 @@ def require_pro(f):
|
|
|
74
80
|
def pro_feature():
|
|
75
81
|
...
|
|
76
82
|
"""
|
|
83
|
+
|
|
77
84
|
@functools.wraps(f)
|
|
78
85
|
def wrapper(*args, **kwargs):
|
|
79
86
|
from souleyez.auth import get_current_user, Tier
|
|
80
87
|
|
|
81
88
|
user = get_current_user()
|
|
82
89
|
if user is None or user.tier != Tier.PRO:
|
|
83
|
-
click.echo(click.style("\n💎 Pro license required.", fg=
|
|
84
|
-
click.echo(
|
|
90
|
+
click.echo(click.style("\n💎 Pro license required.", fg="yellow"))
|
|
91
|
+
click.echo(
|
|
92
|
+
click.style(" Upgrade at: cybersoulsecurity.com/upgrade", fg="cyan")
|
|
93
|
+
)
|
|
85
94
|
sys.exit(1)
|
|
86
95
|
|
|
87
96
|
return f(*args, **kwargs)
|
|
97
|
+
|
|
88
98
|
return wrapper
|
|
89
99
|
|
|
90
100
|
|
|
@@ -106,21 +116,26 @@ def unlock_credentials_if_needed():
|
|
|
106
116
|
return True
|
|
107
117
|
|
|
108
118
|
# Need to unlock
|
|
109
|
-
click.echo(click.style("🔒 Credentials are encrypted.", fg=
|
|
119
|
+
click.echo(click.style("🔒 Credentials are encrypted.", fg="yellow"))
|
|
110
120
|
|
|
111
121
|
max_attempts = 3
|
|
112
122
|
for attempt in range(max_attempts):
|
|
113
123
|
try:
|
|
114
124
|
password = getpass.getpass("Enter master password: ")
|
|
115
125
|
if crypto.unlock(password):
|
|
116
|
-
click.echo(click.style("✅ Unlocked successfully!", fg=
|
|
126
|
+
click.echo(click.style("✅ Unlocked successfully!", fg="green"))
|
|
117
127
|
return True
|
|
118
128
|
else:
|
|
119
129
|
remaining = max_attempts - attempt - 1
|
|
120
130
|
if remaining > 0:
|
|
121
|
-
click.echo(
|
|
131
|
+
click.echo(
|
|
132
|
+
click.style(
|
|
133
|
+
f"❌ Incorrect password. {remaining} attempts remaining.",
|
|
134
|
+
fg="red",
|
|
135
|
+
)
|
|
136
|
+
)
|
|
122
137
|
else:
|
|
123
|
-
click.echo(click.style("❌ Access denied.", fg=
|
|
138
|
+
click.echo(click.style("❌ Access denied.", fg="red"))
|
|
124
139
|
except KeyboardInterrupt:
|
|
125
140
|
click.echo("\n❌ Cancelled by user.")
|
|
126
141
|
return False
|
|
@@ -153,11 +168,22 @@ def require_password(f):
|
|
|
153
168
|
Returns:
|
|
154
169
|
The wrapped function that requires authentication
|
|
155
170
|
"""
|
|
171
|
+
|
|
156
172
|
@functools.wraps(f)
|
|
157
173
|
def wrapper(*args, **kwargs):
|
|
158
174
|
if not unlock_credentials_if_needed():
|
|
159
|
-
click.echo(
|
|
160
|
-
|
|
175
|
+
click.echo(
|
|
176
|
+
click.style(
|
|
177
|
+
"\n⚠️ Authentication required to access this command.", fg="yellow"
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
click.echo(
|
|
181
|
+
click.style(
|
|
182
|
+
" This command accesses sensitive data and requires master password.",
|
|
183
|
+
fg="yellow",
|
|
184
|
+
)
|
|
185
|
+
)
|
|
161
186
|
sys.exit(1)
|
|
162
187
|
return f(*args, **kwargs)
|
|
188
|
+
|
|
163
189
|
return wrapper
|