souleyez 2.43.29__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of souleyez might be problematic. Click here for more details.
- 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
|
@@ -1,34 +1,35 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Evidence linking for deliverables.
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from typing import List, Dict, Optional
|
|
5
6
|
from .database import get_db
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class EvidenceManager:
|
|
9
10
|
"""Manage evidence links for deliverables."""
|
|
10
|
-
|
|
11
|
+
|
|
11
12
|
def __init__(self):
|
|
12
13
|
self.db = get_db()
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
def link_evidence(
|
|
15
16
|
self,
|
|
16
17
|
deliverable_id: int,
|
|
17
18
|
evidence_type: str,
|
|
18
19
|
evidence_id: int,
|
|
19
20
|
linked_by: str = None,
|
|
20
|
-
notes: str = None
|
|
21
|
+
notes: str = None,
|
|
21
22
|
) -> int:
|
|
22
23
|
"""
|
|
23
24
|
Link evidence to deliverable.
|
|
24
|
-
|
|
25
|
+
|
|
25
26
|
Args:
|
|
26
27
|
deliverable_id: Target deliverable
|
|
27
28
|
evidence_type: 'finding', 'screenshot', 'credential', 'job'
|
|
28
29
|
evidence_id: ID of the evidence item
|
|
29
30
|
linked_by: Username who created the link
|
|
30
31
|
notes: Optional notes about the link
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
Returns:
|
|
33
34
|
Link ID
|
|
34
35
|
"""
|
|
@@ -36,82 +37,80 @@ class EvidenceManager:
|
|
|
36
37
|
existing = self.db.execute_one(
|
|
37
38
|
"""SELECT id FROM deliverable_evidence
|
|
38
39
|
WHERE deliverable_id = ? AND evidence_type = ? AND evidence_id = ?""",
|
|
39
|
-
(deliverable_id, evidence_type, evidence_id)
|
|
40
|
+
(deliverable_id, evidence_type, evidence_id),
|
|
40
41
|
)
|
|
41
|
-
|
|
42
|
+
|
|
42
43
|
if existing:
|
|
43
|
-
return existing[
|
|
44
|
-
|
|
45
|
-
return self.db.insert(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
return existing["id"] # Already linked
|
|
45
|
+
|
|
46
|
+
return self.db.insert(
|
|
47
|
+
"deliverable_evidence",
|
|
48
|
+
{
|
|
49
|
+
"deliverable_id": deliverable_id,
|
|
50
|
+
"evidence_type": evidence_type,
|
|
51
|
+
"evidence_id": evidence_id,
|
|
52
|
+
"linked_by": linked_by,
|
|
53
|
+
"notes": notes,
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
|
|
53
57
|
def get_evidence(self, deliverable_id: int) -> Dict:
|
|
54
58
|
"""
|
|
55
59
|
Get all evidence for deliverable, grouped by type.
|
|
56
|
-
|
|
60
|
+
|
|
57
61
|
Returns:
|
|
58
62
|
Dict with keys: findings, screenshots, credentials, jobs
|
|
59
63
|
"""
|
|
60
64
|
links = self.db.execute(
|
|
61
65
|
"SELECT * FROM deliverable_evidence WHERE deliverable_id = ? ORDER BY linked_at DESC",
|
|
62
|
-
(deliverable_id,)
|
|
66
|
+
(deliverable_id,),
|
|
63
67
|
)
|
|
64
|
-
|
|
65
|
-
evidence = {
|
|
66
|
-
|
|
67
|
-
'screenshots': [],
|
|
68
|
-
'credentials': [],
|
|
69
|
-
'jobs': []
|
|
70
|
-
}
|
|
71
|
-
|
|
68
|
+
|
|
69
|
+
evidence = {"findings": [], "screenshots": [], "credentials": [], "jobs": []}
|
|
70
|
+
|
|
72
71
|
for link in links:
|
|
73
|
-
etype = link[
|
|
74
|
-
eid = link[
|
|
75
|
-
|
|
72
|
+
etype = link["evidence_type"]
|
|
73
|
+
eid = link["evidence_id"]
|
|
74
|
+
|
|
76
75
|
# Fetch actual evidence data
|
|
77
|
-
if etype ==
|
|
76
|
+
if etype == "finding":
|
|
78
77
|
finding = self._get_finding(eid)
|
|
79
78
|
if finding:
|
|
80
|
-
finding[
|
|
81
|
-
finding[
|
|
82
|
-
finding[
|
|
83
|
-
evidence[
|
|
84
|
-
|
|
85
|
-
elif etype ==
|
|
79
|
+
finding["_link_notes"] = link["notes"]
|
|
80
|
+
finding["_linked_at"] = link["linked_at"]
|
|
81
|
+
finding["_link_id"] = link["id"]
|
|
82
|
+
evidence["findings"].append(finding)
|
|
83
|
+
|
|
84
|
+
elif etype == "screenshot":
|
|
86
85
|
screenshot = self._get_screenshot(eid)
|
|
87
86
|
if screenshot:
|
|
88
|
-
screenshot[
|
|
89
|
-
screenshot[
|
|
90
|
-
screenshot[
|
|
91
|
-
evidence[
|
|
92
|
-
|
|
93
|
-
elif etype ==
|
|
87
|
+
screenshot["_link_notes"] = link["notes"]
|
|
88
|
+
screenshot["_linked_at"] = link["linked_at"]
|
|
89
|
+
screenshot["_link_id"] = link["id"]
|
|
90
|
+
evidence["screenshots"].append(screenshot)
|
|
91
|
+
|
|
92
|
+
elif etype == "credential":
|
|
94
93
|
credential = self._get_credential(eid)
|
|
95
94
|
if credential:
|
|
96
|
-
credential[
|
|
97
|
-
credential[
|
|
98
|
-
credential[
|
|
99
|
-
evidence[
|
|
100
|
-
|
|
101
|
-
elif etype ==
|
|
95
|
+
credential["_link_notes"] = link["notes"]
|
|
96
|
+
credential["_linked_at"] = link["linked_at"]
|
|
97
|
+
credential["_link_id"] = link["id"]
|
|
98
|
+
evidence["credentials"].append(credential)
|
|
99
|
+
|
|
100
|
+
elif etype == "job":
|
|
102
101
|
job = self._get_job(eid)
|
|
103
102
|
if job:
|
|
104
|
-
job[
|
|
105
|
-
job[
|
|
106
|
-
job[
|
|
107
|
-
evidence[
|
|
108
|
-
|
|
103
|
+
job["_link_notes"] = link["notes"]
|
|
104
|
+
job["_linked_at"] = link["linked_at"]
|
|
105
|
+
job["_link_id"] = link["id"]
|
|
106
|
+
evidence["jobs"].append(job)
|
|
107
|
+
|
|
109
108
|
return evidence
|
|
110
|
-
|
|
109
|
+
|
|
111
110
|
def suggest_evidence(self, deliverable_id: int, engagement_id: int = None) -> Dict:
|
|
112
111
|
"""
|
|
113
112
|
AI-suggest related evidence based on deliverable keywords.
|
|
114
|
-
|
|
113
|
+
|
|
115
114
|
Args:
|
|
116
115
|
deliverable_id: Target deliverable
|
|
117
116
|
engagement_id: Optional engagement filter
|
|
@@ -119,143 +118,184 @@ class EvidenceManager:
|
|
|
119
118
|
Dict with suggested evidence items and confidence scores
|
|
120
119
|
"""
|
|
121
120
|
from .deliverables import DeliverableManager
|
|
121
|
+
|
|
122
122
|
dm = DeliverableManager()
|
|
123
123
|
deliverable = dm.get_deliverable(deliverable_id)
|
|
124
124
|
if not deliverable:
|
|
125
125
|
# Return empty suggestions and helpful error
|
|
126
126
|
return {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
"findings": [],
|
|
128
|
+
"screenshots": [],
|
|
129
|
+
"credentials": [],
|
|
130
|
+
"jobs": [],
|
|
131
|
+
"_error": "Deliverable not found or invalid ID.",
|
|
132
132
|
}
|
|
133
133
|
if not engagement_id:
|
|
134
|
-
engagement_id = deliverable.get(
|
|
134
|
+
engagement_id = deliverable.get("engagement_id")
|
|
135
135
|
# Extract keywords from title and description safely
|
|
136
|
-
title = (deliverable.get(
|
|
137
|
-
description = (deliverable.get(
|
|
136
|
+
title = (deliverable.get("title") or "").lower()
|
|
137
|
+
description = (deliverable.get("description") or "").lower()
|
|
138
138
|
combined_text = f"{title} {description}"
|
|
139
139
|
keywords = self._extract_keywords(combined_text)
|
|
140
|
-
suggestions = {
|
|
141
|
-
|
|
142
|
-
'screenshots': [],
|
|
143
|
-
'credentials': [],
|
|
144
|
-
'jobs': []
|
|
145
|
-
}
|
|
146
|
-
|
|
140
|
+
suggestions = {"findings": [], "screenshots": [], "credentials": [], "jobs": []}
|
|
141
|
+
|
|
147
142
|
# Find related findings
|
|
148
143
|
for keyword in keywords[:5]: # Top 5 keywords
|
|
149
144
|
findings = self.db.execute(
|
|
150
145
|
"""SELECT * FROM findings
|
|
151
146
|
WHERE engagement_id = ? AND LOWER(title) LIKE ?
|
|
152
147
|
LIMIT 10""",
|
|
153
|
-
(engagement_id, f
|
|
148
|
+
(engagement_id, f"%{keyword}%"),
|
|
154
149
|
)
|
|
155
150
|
for f in findings:
|
|
156
151
|
# Avoid duplicates
|
|
157
|
-
if not any(
|
|
158
|
-
f[
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
if not any(
|
|
153
|
+
existing["id"] == f["id"] for existing in suggestions["findings"]
|
|
154
|
+
):
|
|
155
|
+
f["_match_keyword"] = keyword
|
|
156
|
+
f["_confidence"] = self._calculate_confidence(
|
|
157
|
+
combined_text, f.get("title", "")
|
|
158
|
+
)
|
|
159
|
+
suggestions["findings"].append(f)
|
|
160
|
+
|
|
162
161
|
# Find related credentials (if deliverable mentions credentials/users/passwords)
|
|
163
|
-
if any(
|
|
162
|
+
if any(
|
|
163
|
+
kw in combined_text
|
|
164
|
+
for kw in ["credential", "user", "password", "account", "login", "auth"]
|
|
165
|
+
):
|
|
164
166
|
credentials = self.db.execute(
|
|
165
167
|
"SELECT * FROM credentials WHERE engagement_id = ? LIMIT 20",
|
|
166
|
-
(engagement_id,)
|
|
168
|
+
(engagement_id,),
|
|
167
169
|
)
|
|
168
170
|
for c in credentials:
|
|
169
|
-
c[
|
|
170
|
-
suggestions[
|
|
171
|
-
|
|
171
|
+
c["_confidence"] = 70 # Medium confidence for keyword match
|
|
172
|
+
suggestions["credentials"].extend(credentials)
|
|
173
|
+
|
|
172
174
|
# Find related screenshots
|
|
173
175
|
screenshots = self.db.execute(
|
|
174
176
|
"SELECT * FROM screenshots WHERE engagement_id = ? ORDER BY created_at DESC LIMIT 10",
|
|
175
|
-
(engagement_id,)
|
|
177
|
+
(engagement_id,),
|
|
176
178
|
)
|
|
177
179
|
for s in screenshots:
|
|
178
|
-
s[
|
|
179
|
-
suggestions[
|
|
180
|
-
|
|
180
|
+
s["_confidence"] = 50 # Lower confidence for screenshots
|
|
181
|
+
suggestions["screenshots"] = screenshots
|
|
182
|
+
|
|
181
183
|
# Sort findings by confidence
|
|
182
|
-
suggestions[
|
|
183
|
-
|
|
184
|
+
suggestions["findings"].sort(
|
|
185
|
+
key=lambda x: x.get("_confidence", 0), reverse=True
|
|
186
|
+
)
|
|
187
|
+
|
|
184
188
|
# Limit results
|
|
185
|
-
suggestions[
|
|
186
|
-
suggestions[
|
|
187
|
-
|
|
189
|
+
suggestions["findings"] = suggestions["findings"][:10]
|
|
190
|
+
suggestions["credentials"] = suggestions["credentials"][:10]
|
|
191
|
+
|
|
188
192
|
return suggestions
|
|
189
|
-
|
|
190
|
-
def unlink_evidence(
|
|
193
|
+
|
|
194
|
+
def unlink_evidence(
|
|
195
|
+
self, deliverable_id: int, evidence_type: str, evidence_id: int
|
|
196
|
+
) -> bool:
|
|
191
197
|
"""Remove evidence link."""
|
|
192
198
|
self.db.execute(
|
|
193
199
|
"""DELETE FROM deliverable_evidence
|
|
194
200
|
WHERE deliverable_id = ? AND evidence_type = ? AND evidence_id = ?""",
|
|
195
|
-
(deliverable_id, evidence_type, evidence_id)
|
|
201
|
+
(deliverable_id, evidence_type, evidence_id),
|
|
196
202
|
)
|
|
197
203
|
return True
|
|
198
|
-
|
|
204
|
+
|
|
199
205
|
def get_evidence_count(self, deliverable_id: int) -> int:
|
|
200
206
|
"""Get count of linked evidence items."""
|
|
201
207
|
result = self.db.execute_one(
|
|
202
208
|
"SELECT COUNT(*) as count FROM deliverable_evidence WHERE deliverable_id = ?",
|
|
203
|
-
(deliverable_id,)
|
|
209
|
+
(deliverable_id,),
|
|
204
210
|
)
|
|
205
|
-
return result[
|
|
206
|
-
|
|
207
|
-
def get_deliverables_for_evidence(
|
|
211
|
+
return result["count"] if result else 0
|
|
212
|
+
|
|
213
|
+
def get_deliverables_for_evidence(
|
|
214
|
+
self, evidence_type: str, evidence_id: int
|
|
215
|
+
) -> List[Dict]:
|
|
208
216
|
"""Get all deliverables linked to this evidence item."""
|
|
209
217
|
links = self.db.execute(
|
|
210
218
|
"""SELECT * FROM deliverable_evidence
|
|
211
219
|
WHERE evidence_type = ? AND evidence_id = ?""",
|
|
212
|
-
(evidence_type, evidence_id)
|
|
220
|
+
(evidence_type, evidence_id),
|
|
213
221
|
)
|
|
214
|
-
|
|
222
|
+
|
|
215
223
|
deliverables = []
|
|
216
224
|
if links:
|
|
217
225
|
from .deliverables import DeliverableManager
|
|
226
|
+
|
|
218
227
|
dm = DeliverableManager()
|
|
219
|
-
|
|
228
|
+
|
|
220
229
|
for link in links:
|
|
221
|
-
d = dm.get_deliverable(link[
|
|
230
|
+
d = dm.get_deliverable(link["deliverable_id"])
|
|
222
231
|
if d:
|
|
223
|
-
d[
|
|
224
|
-
d[
|
|
232
|
+
d["_link_notes"] = link["notes"]
|
|
233
|
+
d["_linked_at"] = link["linked_at"]
|
|
225
234
|
deliverables.append(d)
|
|
226
|
-
|
|
235
|
+
|
|
227
236
|
return deliverables
|
|
228
|
-
|
|
237
|
+
|
|
229
238
|
def _get_finding(self, finding_id: int) -> Optional[Dict]:
|
|
230
239
|
"""Fetch finding by ID."""
|
|
231
240
|
return self.db.execute_one("SELECT * FROM findings WHERE id = ?", (finding_id,))
|
|
232
|
-
|
|
241
|
+
|
|
233
242
|
def _get_screenshot(self, screenshot_id: int) -> Optional[Dict]:
|
|
234
243
|
"""Fetch screenshot by ID."""
|
|
235
|
-
return self.db.execute_one(
|
|
236
|
-
|
|
244
|
+
return self.db.execute_one(
|
|
245
|
+
"SELECT * FROM screenshots WHERE id = ?", (screenshot_id,)
|
|
246
|
+
)
|
|
247
|
+
|
|
237
248
|
def _get_credential(self, credential_id: int) -> Optional[Dict]:
|
|
238
249
|
"""Fetch credential by ID."""
|
|
239
|
-
return self.db.execute_one(
|
|
240
|
-
|
|
250
|
+
return self.db.execute_one(
|
|
251
|
+
"SELECT * FROM credentials WHERE id = ?", (credential_id,)
|
|
252
|
+
)
|
|
253
|
+
|
|
241
254
|
def _get_job(self, job_id: int) -> Optional[Dict]:
|
|
242
255
|
"""Fetch job by ID."""
|
|
243
256
|
from souleyez.engine.background import list_jobs
|
|
257
|
+
|
|
244
258
|
jobs = list_jobs(limit=1000)
|
|
245
259
|
for job in jobs:
|
|
246
|
-
if job.get(
|
|
260
|
+
if job.get("id") == job_id:
|
|
247
261
|
return job
|
|
248
262
|
return None
|
|
249
|
-
|
|
263
|
+
|
|
250
264
|
def _extract_keywords(self, text: str) -> List[str]:
|
|
251
265
|
"""Extract meaningful keywords from text."""
|
|
252
266
|
stopwords = {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
267
|
+
"the",
|
|
268
|
+
"a",
|
|
269
|
+
"an",
|
|
270
|
+
"and",
|
|
271
|
+
"or",
|
|
272
|
+
"but",
|
|
273
|
+
"in",
|
|
274
|
+
"on",
|
|
275
|
+
"at",
|
|
276
|
+
"to",
|
|
277
|
+
"for",
|
|
278
|
+
"of",
|
|
279
|
+
"with",
|
|
280
|
+
"from",
|
|
281
|
+
"by",
|
|
282
|
+
"as",
|
|
283
|
+
"is",
|
|
284
|
+
"are",
|
|
285
|
+
"was",
|
|
286
|
+
"were",
|
|
287
|
+
"be",
|
|
288
|
+
"this",
|
|
289
|
+
"that",
|
|
290
|
+
"which",
|
|
291
|
+
"what",
|
|
292
|
+
"when",
|
|
293
|
+
"where",
|
|
294
|
+
"who",
|
|
295
|
+
"how",
|
|
256
296
|
}
|
|
257
297
|
# Clean and split text
|
|
258
|
-
words = text.lower().replace(
|
|
298
|
+
words = text.lower().replace("-", " ").replace("_", " ").split()
|
|
259
299
|
# Filter stopwords and short words
|
|
260
300
|
keywords = [w for w in words if len(w) > 3 and w not in stopwords]
|
|
261
301
|
# Remove duplicates while preserving order
|
|
@@ -266,34 +306,43 @@ class EvidenceManager:
|
|
|
266
306
|
seen.add(kw)
|
|
267
307
|
unique_keywords.append(kw)
|
|
268
308
|
return unique_keywords
|
|
269
|
-
|
|
309
|
+
|
|
270
310
|
def _calculate_confidence(self, deliverable_text: str, finding_title: str) -> int:
|
|
271
311
|
"""
|
|
272
312
|
Calculate match confidence (0-100).
|
|
273
|
-
|
|
313
|
+
|
|
274
314
|
Higher score means better match between deliverable and finding.
|
|
275
315
|
"""
|
|
276
316
|
if not finding_title:
|
|
277
317
|
return 30 # Low confidence
|
|
278
|
-
|
|
318
|
+
|
|
279
319
|
d_words = set(deliverable_text.lower().split())
|
|
280
320
|
f_words = set(finding_title.lower().split())
|
|
281
|
-
|
|
321
|
+
|
|
282
322
|
common = d_words & f_words
|
|
283
323
|
if not common:
|
|
284
324
|
return 30 # Low confidence
|
|
285
|
-
|
|
325
|
+
|
|
286
326
|
# Calculate Jaccard similarity
|
|
287
327
|
union = d_words | f_words
|
|
288
328
|
ratio = len(common) / len(union) if union else 0
|
|
289
|
-
|
|
329
|
+
|
|
290
330
|
# Boost score if important security terms match
|
|
291
|
-
important_terms = {
|
|
292
|
-
|
|
331
|
+
important_terms = {
|
|
332
|
+
"sql",
|
|
333
|
+
"xss",
|
|
334
|
+
"injection",
|
|
335
|
+
"authentication",
|
|
336
|
+
"authorization",
|
|
337
|
+
"encryption",
|
|
338
|
+
"credential",
|
|
339
|
+
"vulnerability",
|
|
340
|
+
"exploit",
|
|
341
|
+
}
|
|
293
342
|
important_matches = common & important_terms
|
|
294
343
|
boost = len(important_matches) * 10
|
|
295
|
-
|
|
344
|
+
|
|
296
345
|
base_score = int(ratio * 100)
|
|
297
346
|
final_score = min(100, base_score + boost)
|
|
298
|
-
|
|
347
|
+
|
|
299
348
|
return final_score
|