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/storage/database.py
CHANGED
|
@@ -12,15 +12,13 @@ from souleyez import config
|
|
|
12
12
|
from souleyez.log_config import get_logger
|
|
13
13
|
|
|
14
14
|
logger = get_logger(__name__)
|
|
15
|
-
DB_PATH = Path(config.get(
|
|
15
|
+
DB_PATH = Path(config.get("database.path", "~/.souleyez/souleyez.db")).expanduser()
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class Database:
|
|
19
19
|
def __init__(self, db_path: str = None):
|
|
20
20
|
self.db_path = db_path or str(DB_PATH)
|
|
21
|
-
logger.info("Initializing database", extra={
|
|
22
|
-
"db_path": self.db_path
|
|
23
|
-
})
|
|
21
|
+
logger.info("Initializing database", extra={"db_path": self.db_path})
|
|
24
22
|
self._ensure_db()
|
|
25
23
|
|
|
26
24
|
def _ensure_db(self):
|
|
@@ -41,10 +39,10 @@ class Database:
|
|
|
41
39
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
42
40
|
conn.execute("PRAGMA busy_timeout=30000")
|
|
43
41
|
|
|
44
|
-
logger.info(
|
|
45
|
-
"
|
|
46
|
-
"foreign_keys": True
|
|
47
|
-
|
|
42
|
+
logger.info(
|
|
43
|
+
"Database connection established",
|
|
44
|
+
extra={"wal_mode": True, "foreign_keys": True},
|
|
45
|
+
)
|
|
48
46
|
|
|
49
47
|
# Check if this is an existing database (has tables)
|
|
50
48
|
cursor = conn.execute(
|
|
@@ -58,22 +56,28 @@ class Database:
|
|
|
58
56
|
if is_existing_db:
|
|
59
57
|
try:
|
|
60
58
|
from .migrations.migration_manager import MigrationManager
|
|
59
|
+
|
|
61
60
|
manager = MigrationManager(self.db_path)
|
|
62
61
|
pending = manager.get_pending_migrations()
|
|
63
62
|
if pending:
|
|
64
|
-
logger.info(
|
|
65
|
-
"
|
|
66
|
-
|
|
63
|
+
logger.info(
|
|
64
|
+
"Running pending migrations for existing database",
|
|
65
|
+
extra={"pending_count": len(pending)},
|
|
66
|
+
)
|
|
67
67
|
manager.migrate()
|
|
68
|
-
logger.info(
|
|
69
|
-
"
|
|
70
|
-
|
|
68
|
+
logger.info(
|
|
69
|
+
"Migrations completed successfully",
|
|
70
|
+
extra={"count": len(pending)},
|
|
71
|
+
)
|
|
71
72
|
except Exception as migration_error:
|
|
72
|
-
logger.error(
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
logger.error(
|
|
74
|
+
"Failed to run migrations on existing database",
|
|
75
|
+
extra={
|
|
76
|
+
"error": str(migration_error),
|
|
77
|
+
"error_type": type(migration_error).__name__,
|
|
78
|
+
"traceback": traceback.format_exc(),
|
|
79
|
+
},
|
|
80
|
+
)
|
|
77
81
|
raise # Don't continue if migrations fail for existing DB
|
|
78
82
|
|
|
79
83
|
# Reconnect for schema loading
|
|
@@ -87,19 +91,21 @@ class Database:
|
|
|
87
91
|
schema_path = Path(__file__).parent / "schema.sql"
|
|
88
92
|
|
|
89
93
|
if schema_path.exists():
|
|
90
|
-
with open(schema_path,
|
|
94
|
+
with open(schema_path, "r") as f:
|
|
91
95
|
schema_sql = f.read()
|
|
92
96
|
conn.executescript(schema_sql)
|
|
93
|
-
logger.info(
|
|
94
|
-
"schema_file": str(schema_path)
|
|
95
|
-
|
|
97
|
+
logger.info(
|
|
98
|
+
"Schema loaded from file", extra={"schema_file": str(schema_path)}
|
|
99
|
+
)
|
|
96
100
|
else:
|
|
97
101
|
# If schema file doesn't exist, create essential tables inline
|
|
98
102
|
# This must match schema.sql structure for migrations to work
|
|
99
|
-
logger.warning(
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
logger.warning(
|
|
104
|
+
"Schema file not found, using inline schema",
|
|
105
|
+
extra={"expected_path": str(schema_path)},
|
|
106
|
+
)
|
|
107
|
+
conn.executescript(
|
|
108
|
+
"""
|
|
103
109
|
CREATE TABLE IF NOT EXISTS engagements (
|
|
104
110
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
105
111
|
name TEXT UNIQUE NOT NULL,
|
|
@@ -261,7 +267,8 @@ class Database:
|
|
|
261
267
|
CREATE INDEX IF NOT EXISTS idx_credentials_engagement ON credentials(engagement_id);
|
|
262
268
|
CREATE INDEX IF NOT EXISTS idx_credentials_host ON credentials(host_id);
|
|
263
269
|
CREATE INDEX IF NOT EXISTS idx_credentials_status ON credentials(status);
|
|
264
|
-
"""
|
|
270
|
+
"""
|
|
271
|
+
)
|
|
265
272
|
|
|
266
273
|
conn.commit()
|
|
267
274
|
conn.close()
|
|
@@ -271,29 +278,35 @@ class Database:
|
|
|
271
278
|
if not is_existing_db:
|
|
272
279
|
try:
|
|
273
280
|
from .migrations.migration_manager import MigrationManager
|
|
281
|
+
|
|
274
282
|
manager = MigrationManager(self.db_path)
|
|
275
283
|
pending = manager.get_pending_migrations()
|
|
276
284
|
if pending:
|
|
277
|
-
logger.info(
|
|
278
|
-
"
|
|
279
|
-
|
|
285
|
+
logger.info(
|
|
286
|
+
"Running pending migrations for fresh database",
|
|
287
|
+
extra={"pending_count": len(pending)},
|
|
288
|
+
)
|
|
280
289
|
manager.migrate()
|
|
281
|
-
logger.info(
|
|
282
|
-
"
|
|
283
|
-
|
|
290
|
+
logger.info(
|
|
291
|
+
"Migrations completed successfully",
|
|
292
|
+
extra={"count": len(pending)},
|
|
293
|
+
)
|
|
284
294
|
except Exception as migration_error:
|
|
285
|
-
logger.error(
|
|
286
|
-
"
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
295
|
+
logger.error(
|
|
296
|
+
"Failed to run migrations",
|
|
297
|
+
extra={
|
|
298
|
+
"error": str(migration_error),
|
|
299
|
+
"error_type": type(migration_error).__name__,
|
|
300
|
+
"traceback": traceback.format_exc(),
|
|
301
|
+
},
|
|
302
|
+
)
|
|
290
303
|
|
|
291
304
|
except Exception as e:
|
|
292
|
-
logger.error(
|
|
293
|
-
"
|
|
294
|
-
"traceback": traceback.format_exc()
|
|
295
|
-
|
|
296
|
-
if
|
|
305
|
+
logger.error(
|
|
306
|
+
"Database initialization failed",
|
|
307
|
+
extra={"error": str(e), "traceback": traceback.format_exc()},
|
|
308
|
+
)
|
|
309
|
+
if "conn" in locals():
|
|
297
310
|
try:
|
|
298
311
|
conn.close()
|
|
299
312
|
except:
|
|
@@ -319,7 +332,13 @@ class Database:
|
|
|
319
332
|
conn.execute(f"PRAGMA busy_timeout={busy_timeout}")
|
|
320
333
|
return conn
|
|
321
334
|
|
|
322
|
-
def execute(
|
|
335
|
+
def execute(
|
|
336
|
+
self,
|
|
337
|
+
query: str,
|
|
338
|
+
params: tuple = None,
|
|
339
|
+
retries: int = 3,
|
|
340
|
+
fast_mode: bool = False,
|
|
341
|
+
) -> List[Dict[str, Any]]:
|
|
323
342
|
"""
|
|
324
343
|
Execute query and return results with retry logic.
|
|
325
344
|
|
|
@@ -346,13 +365,17 @@ class Database:
|
|
|
346
365
|
|
|
347
366
|
return results
|
|
348
367
|
except sqlite3.OperationalError as e:
|
|
349
|
-
if
|
|
368
|
+
if (
|
|
369
|
+
"locked" in str(e).lower()
|
|
370
|
+
and attempt < max_retries - 1
|
|
371
|
+
and not fast_mode
|
|
372
|
+
):
|
|
350
373
|
time.sleep(0.1 * (attempt + 1)) # Exponential backoff
|
|
351
374
|
continue
|
|
352
375
|
# For fast_mode, don't retry - just raise immediately
|
|
353
376
|
raise
|
|
354
377
|
except Exception:
|
|
355
|
-
if
|
|
378
|
+
if "conn" in locals():
|
|
356
379
|
conn.close()
|
|
357
380
|
raise
|
|
358
381
|
|
|
@@ -363,8 +386,8 @@ class Database:
|
|
|
363
386
|
|
|
364
387
|
def insert(self, table: str, data: Dict[str, Any], retries: int = 3) -> int:
|
|
365
388
|
"""Insert row and return ID with retry logic."""
|
|
366
|
-
columns =
|
|
367
|
-
placeholders =
|
|
389
|
+
columns = ", ".join(data.keys())
|
|
390
|
+
placeholders = ", ".join(["?" for _ in data])
|
|
368
391
|
query = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})"
|
|
369
392
|
|
|
370
393
|
for attempt in range(retries):
|
|
@@ -383,7 +406,7 @@ class Database:
|
|
|
383
406
|
continue
|
|
384
407
|
raise
|
|
385
408
|
except Exception:
|
|
386
|
-
if
|
|
409
|
+
if "conn" in locals():
|
|
387
410
|
conn.close()
|
|
388
411
|
raise
|
|
389
412
|
|
souleyez/storage/db.py
CHANGED
|
@@ -17,7 +17,7 @@ from typing import Optional, List, Dict, Any
|
|
|
17
17
|
from ..utils import ensure_dirs, HISTORY_FILE, read_json
|
|
18
18
|
from .. import config
|
|
19
19
|
|
|
20
|
-
DB_PATH = Path(config.get(
|
|
20
|
+
DB_PATH = Path(config.get("database.path", "~/.souleyez/souleyez.db")).expanduser()
|
|
21
21
|
ensure_dirs()
|
|
22
22
|
|
|
23
23
|
|
|
@@ -41,7 +41,8 @@ def _get_conn(fast_mode: bool = False):
|
|
|
41
41
|
def init_db():
|
|
42
42
|
conn = _get_conn()
|
|
43
43
|
cur = conn.cursor()
|
|
44
|
-
cur.executescript(
|
|
44
|
+
cur.executescript(
|
|
45
|
+
"""
|
|
45
46
|
CREATE TABLE IF NOT EXISTS scans (
|
|
46
47
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
47
48
|
ts TEXT,
|
|
@@ -59,13 +60,15 @@ def init_db():
|
|
|
59
60
|
CREATE INDEX IF NOT EXISTS idx_scans_tool ON scans(tool);
|
|
60
61
|
CREATE INDEX IF NOT EXISTS idx_scans_target ON scans(target);
|
|
61
62
|
CREATE INDEX IF NOT EXISTS idx_scans_ts ON scans(ts);
|
|
62
|
-
"""
|
|
63
|
+
"""
|
|
64
|
+
)
|
|
63
65
|
conn.commit()
|
|
64
66
|
conn.close()
|
|
65
|
-
|
|
67
|
+
|
|
66
68
|
# Auto-apply pending migrations on first run or database initialization
|
|
67
69
|
try:
|
|
68
70
|
from .migrations.migration_manager import MigrationManager
|
|
71
|
+
|
|
69
72
|
manager = MigrationManager(str(DB_PATH))
|
|
70
73
|
pending = manager.get_pending_migrations()
|
|
71
74
|
if pending:
|
|
@@ -84,22 +87,25 @@ def insert_scan(entry: Dict[str, Any]) -> int:
|
|
|
84
87
|
"""
|
|
85
88
|
conn = _get_conn()
|
|
86
89
|
cur = conn.cursor()
|
|
87
|
-
cur.execute(
|
|
90
|
+
cur.execute(
|
|
91
|
+
"""
|
|
88
92
|
INSERT INTO scans (ts, tool, target, label, args, log_path, raw_path, summary, per_host, status, rc)
|
|
89
93
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
90
|
-
""",
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
""",
|
|
95
|
+
(
|
|
96
|
+
entry.get("ts"),
|
|
97
|
+
entry.get("tool"),
|
|
98
|
+
entry.get("target"),
|
|
99
|
+
entry.get("label"),
|
|
100
|
+
json.dumps(entry.get("args") or []),
|
|
101
|
+
entry.get("log"),
|
|
102
|
+
entry.get("xml"),
|
|
103
|
+
json.dumps(entry.get("summary") or {}),
|
|
104
|
+
json.dumps(entry.get("per_host") or []),
|
|
105
|
+
entry.get("status") or "pending",
|
|
106
|
+
entry.get("rc"),
|
|
107
|
+
),
|
|
108
|
+
)
|
|
103
109
|
conn.commit()
|
|
104
110
|
rowid = cur.lastrowid
|
|
105
111
|
conn.close()
|
|
@@ -121,7 +127,19 @@ def update_scan(scan_id: int, **fields):
|
|
|
121
127
|
k_db = "raw_path"
|
|
122
128
|
else:
|
|
123
129
|
k_db = k
|
|
124
|
-
if k_db not in (
|
|
130
|
+
if k_db not in (
|
|
131
|
+
"ts",
|
|
132
|
+
"tool",
|
|
133
|
+
"target",
|
|
134
|
+
"label",
|
|
135
|
+
"args",
|
|
136
|
+
"log_path",
|
|
137
|
+
"raw_path",
|
|
138
|
+
"summary",
|
|
139
|
+
"per_host",
|
|
140
|
+
"status",
|
|
141
|
+
"rc",
|
|
142
|
+
):
|
|
125
143
|
continue
|
|
126
144
|
if k_db in ("args", "summary", "per_host"):
|
|
127
145
|
set_clauses.append(f"{k_db} = ?")
|
|
@@ -144,27 +162,31 @@ def get_scans(limit: int = 100, tool: Optional[str] = None) -> List[Dict[str, An
|
|
|
144
162
|
conn = _get_conn()
|
|
145
163
|
cur = conn.cursor()
|
|
146
164
|
if tool:
|
|
147
|
-
cur.execute(
|
|
165
|
+
cur.execute(
|
|
166
|
+
"SELECT * FROM scans WHERE tool = ? ORDER BY id DESC LIMIT ?", (tool, limit)
|
|
167
|
+
)
|
|
148
168
|
else:
|
|
149
169
|
cur.execute("SELECT * FROM scans ORDER BY id DESC LIMIT ?", (limit,))
|
|
150
170
|
rows = cur.fetchall()
|
|
151
171
|
conn.close()
|
|
152
172
|
out = []
|
|
153
173
|
for r in rows:
|
|
154
|
-
out.append(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
174
|
+
out.append(
|
|
175
|
+
{
|
|
176
|
+
"id": r["id"],
|
|
177
|
+
"ts": r["ts"],
|
|
178
|
+
"tool": r["tool"],
|
|
179
|
+
"target": r["target"],
|
|
180
|
+
"label": r["label"],
|
|
181
|
+
"args": json.loads(r["args"] or "[]"),
|
|
182
|
+
"log": r["log_path"],
|
|
183
|
+
"xml": r["raw_path"],
|
|
184
|
+
"summary": json.loads(r["summary"] or "{}"),
|
|
185
|
+
"per_host": json.loads(r["per_host"] or "[]"),
|
|
186
|
+
"status": r["status"],
|
|
187
|
+
"rc": r["rc"],
|
|
188
|
+
}
|
|
189
|
+
)
|
|
168
190
|
return out
|
|
169
191
|
|
|
170
192
|
|
|
@@ -188,7 +210,7 @@ def get_scan(scan_id: int) -> Optional[Dict[str, Any]]:
|
|
|
188
210
|
"summary": json.loads(row["summary"] or "{}"),
|
|
189
211
|
"per_host": json.loads(row["per_host"] or "[]"),
|
|
190
212
|
"status": row["status"],
|
|
191
|
-
"rc": row["rc"]
|
|
213
|
+
"rc": row["rc"],
|
|
192
214
|
}
|
|
193
215
|
|
|
194
216
|
|
|
@@ -215,7 +237,7 @@ def import_json_history_to_db():
|
|
|
215
237
|
"log": e.get("log"),
|
|
216
238
|
"xml": e.get("xml"),
|
|
217
239
|
"summary": e.get("summary") or {},
|
|
218
|
-
"per_host": (e.get("summary") or {}).get("per_host") or []
|
|
240
|
+
"per_host": (e.get("summary") or {}).get("per_host") or [],
|
|
219
241
|
}
|
|
220
242
|
insert_scan(entry)
|
|
221
243
|
inserted += 1
|