souleyez 3.0.0__py3-none-any.whl → 3.0.9__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 -1
- souleyez/ai/__init__.py +7 -7
- souleyez/ai/action_mapper.py +3 -2
- souleyez/ai/chain_advisor.py +2 -1
- souleyez/ai/claude_provider.py +2 -2
- souleyez/ai/context_builder.py +4 -2
- souleyez/ai/executor.py +9 -6
- souleyez/ai/feedback_handler.py +4 -2
- souleyez/ai/llm_provider.py +2 -2
- souleyez/ai/ollama_provider.py +2 -2
- souleyez/ai/ollama_service.py +10 -26
- souleyez/ai/path_scorer.py +2 -1
- souleyez/ai/recommender.py +6 -4
- souleyez/ai/report_context.py +2 -2
- souleyez/ai/report_service.py +5 -5
- souleyez/ai/result_parser.py +3 -2
- souleyez/ai/safety.py +5 -2
- souleyez/auth/__init__.py +6 -6
- souleyez/auth/audit.py +2 -2
- souleyez/auth/engagement_access.py +5 -7
- souleyez/auth/permissions.py +1 -1
- souleyez/auth/session_manager.py +5 -5
- souleyez/auth/user_manager.py +4 -5
- souleyez/commands/audit.py +6 -5
- souleyez/commands/auth.py +6 -5
- souleyez/commands/deliverables.py +2 -3
- souleyez/commands/engagement.py +3 -3
- souleyez/commands/license.py +3 -2
- souleyez/commands/screenshots.py +5 -4
- souleyez/commands/user.py +10 -8
- souleyez/config.py +4 -2
- souleyez/core/credential_tester.py +4 -2
- souleyez/core/cve_mappings.py +2 -1
- souleyez/core/cve_matcher.py +2 -1
- souleyez/core/msf_auto_mapper.py +2 -0
- souleyez/core/msf_chain_engine.py +3 -1
- souleyez/core/msf_database.py +7 -13
- souleyez/core/msf_integration.py +2 -2
- souleyez/core/msf_rpc_client.py +3 -2
- souleyez/core/msf_rpc_manager.py +4 -4
- souleyez/core/msf_sync_manager.py +7 -7
- souleyez/core/network_utils.py +1 -1
- souleyez/core/parser_handler.py +2 -1
- souleyez/core/pending_chains.py +4 -3
- souleyez/core/templates.py +5 -2
- souleyez/core/tool_chaining.py +101 -70
- souleyez/core/version_utils.py +1 -0
- souleyez/core/vuln_correlation.py +3 -2
- souleyez/core/web_utils.py +2 -1
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +1 -1
- souleyez/detection/mitre_mappings.py +1 -2
- souleyez/detection/validator.py +5 -4
- souleyez/devtools.py +4 -2
- souleyez/docs/README.md +2 -2
- souleyez/engine/background.py +168 -7
- souleyez/engine/base.py +2 -1
- souleyez/engine/loader.py +4 -2
- souleyez/engine/log_sanitizer.py +1 -0
- souleyez/engine/manager.py +3 -1
- souleyez/engine/result_handler.py +50 -67
- souleyez/engine/worker_manager.py +6 -4
- souleyez/export/evidence_bundle.py +1 -0
- souleyez/handlers/base.py +1 -0
- souleyez/handlers/bash_handler.py +1 -0
- souleyez/handlers/bloodhound_handler.py +1 -0
- souleyez/handlers/certipy_handler.py +1 -0
- souleyez/handlers/crackmapexec_handler.py +2 -20
- souleyez/handlers/dnsrecon_handler.py +2 -1
- souleyez/handlers/enum4linux_handler.py +65 -37
- souleyez/handlers/evil_winrm_handler.py +1 -0
- souleyez/handlers/ffuf_handler.py +3 -1
- souleyez/handlers/gobuster_handler.py +7 -6
- souleyez/handlers/gpp_extract_handler.py +1 -0
- souleyez/handlers/hashcat_handler.py +1 -0
- souleyez/handlers/hydra_handler.py +5 -2
- souleyez/handlers/impacket_getuserspns_handler.py +1 -0
- souleyez/handlers/impacket_psexec_handler.py +1 -0
- souleyez/handlers/impacket_secretsdump_handler.py +1 -0
- souleyez/handlers/john_handler.py +1 -0
- souleyez/handlers/katana_handler.py +39 -2
- souleyez/handlers/kerbrute_handler.py +1 -0
- souleyez/handlers/ldapsearch_handler.py +90 -17
- souleyez/handlers/lfi_extract_handler.py +1 -0
- souleyez/handlers/msf_auxiliary_handler.py +1 -0
- souleyez/handlers/msf_exploit_handler.py +1 -0
- souleyez/handlers/nikto_handler.py +2 -1
- souleyez/handlers/nmap_handler.py +2 -1
- souleyez/handlers/nuclei_handler.py +2 -1
- souleyez/handlers/nxc_handler.py +3 -18
- souleyez/handlers/rdp_sec_check_handler.py +1 -0
- souleyez/handlers/registry.py +1 -0
- souleyez/handlers/responder_handler.py +1 -0
- souleyez/handlers/service_explorer_handler.py +2 -1
- souleyez/handlers/smbclient_handler.py +1 -0
- souleyez/handlers/smbmap_handler.py +3 -2
- souleyez/handlers/sqlmap_handler.py +6 -4
- souleyez/handlers/theharvester_handler.py +2 -1
- souleyez/handlers/web_login_test_handler.py +1 -0
- souleyez/handlers/whois_handler.py +3 -2
- souleyez/handlers/wpscan_handler.py +2 -1
- souleyez/history.py +4 -3
- souleyez/importers/msf_importer.py +5 -3
- souleyez/importers/smart_importer.py +6 -4
- souleyez/integrations/siem/__init__.py +6 -6
- souleyez/integrations/siem/base.py +1 -1
- souleyez/integrations/siem/elastic.py +3 -3
- souleyez/integrations/siem/factory.py +1 -2
- souleyez/integrations/siem/googlesecops.py +4 -4
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +1 -1
- souleyez/integrations/siem/sentinel.py +3 -3
- souleyez/integrations/siem/splunk.py +3 -3
- souleyez/integrations/siem/wazuh.py +4 -4
- souleyez/integrations/wazuh/__init__.py +1 -1
- souleyez/integrations/wazuh/client.py +3 -2
- souleyez/integrations/wazuh/config.py +3 -2
- souleyez/integrations/wazuh/host_mapper.py +3 -1
- souleyez/integrations/wazuh/sync.py +4 -1
- souleyez/intelligence/__init__.py +1 -1
- souleyez/intelligence/correlation_analyzer.py +6 -5
- souleyez/intelligence/exploit_knowledge.py +4 -4
- souleyez/intelligence/exploit_suggestions.py +4 -3
- souleyez/intelligence/gap_analyzer.py +5 -3
- souleyez/intelligence/gap_detector.py +2 -0
- souleyez/intelligence/sensitive_tables.py +1 -1
- souleyez/intelligence/service_parser.py +1 -0
- souleyez/intelligence/surface_analyzer.py +9 -9
- souleyez/intelligence/target_parser.py +1 -0
- souleyez/licensing/__init__.py +3 -3
- souleyez/main.py +25 -18
- souleyez/migrations/fix_job_counter.py +2 -1
- souleyez/parsers/bloodhound_parser.py +1 -0
- souleyez/parsers/crackmapexec_parser.py +2 -1
- souleyez/parsers/dalfox_parser.py +3 -2
- souleyez/parsers/dnsrecon_parser.py +2 -1
- souleyez/parsers/enum4linux_parser.py +2 -1
- souleyez/parsers/ffuf_parser.py +2 -1
- souleyez/parsers/gobuster_parser.py +2 -1
- souleyez/parsers/hashcat_parser.py +3 -2
- souleyez/parsers/http_fingerprint_parser.py +2 -1
- souleyez/parsers/hydra_parser.py +2 -1
- souleyez/parsers/impacket_parser.py +2 -1
- souleyez/parsers/john_parser.py +4 -3
- souleyez/parsers/katana_parser.py +134 -2
- souleyez/parsers/msf_parser.py +2 -1
- souleyez/parsers/nikto_parser.py +2 -1
- souleyez/parsers/nmap_parser.py +14 -3
- souleyez/parsers/nuclei_parser.py +3 -2
- souleyez/parsers/responder_parser.py +1 -0
- souleyez/parsers/searchsploit_parser.py +3 -2
- souleyez/parsers/service_explorer_parser.py +1 -0
- souleyez/parsers/smbmap_parser.py +2 -1
- souleyez/parsers/sqlmap_parser.py +36 -2
- souleyez/parsers/theharvester_parser.py +2 -1
- souleyez/parsers/whois_parser.py +2 -1
- souleyez/parsers/wpscan_parser.py +3 -2
- souleyez/plugins/afp.py +3 -1
- souleyez/plugins/afp_brute.py +3 -1
- souleyez/plugins/ard.py +3 -1
- souleyez/plugins/bloodhound.py +3 -2
- souleyez/plugins/certipy.py +1 -0
- souleyez/plugins/crackmapexec.py +11 -7
- souleyez/plugins/dalfox.py +5 -2
- souleyez/plugins/dns_hijack.py +3 -1
- souleyez/plugins/dnsrecon.py +3 -1
- souleyez/plugins/enum4linux.py +3 -1
- souleyez/plugins/evil_winrm.py +1 -0
- souleyez/plugins/ffuf.py +3 -1
- souleyez/plugins/firmware_extract.py +3 -2
- souleyez/plugins/gobuster.py +6 -3
- souleyez/plugins/gpp_extract.py +1 -0
- souleyez/plugins/hashcat.py +2 -1
- souleyez/plugins/http_fingerprint.py +57 -7
- souleyez/plugins/hydra.py +5 -3
- souleyez/plugins/impacket_common.py +40 -0
- souleyez/plugins/impacket_getnpusers.py +19 -2
- souleyez/plugins/impacket_getuserspns.py +158 -0
- souleyez/plugins/impacket_psexec.py +19 -2
- souleyez/plugins/impacket_secretsdump.py +19 -2
- souleyez/plugins/impacket_smbclient.py +19 -2
- souleyez/plugins/john.py +2 -1
- souleyez/plugins/katana.py +48 -6
- souleyez/plugins/kerbrute.py +1 -0
- souleyez/plugins/lfi_extract.py +1 -0
- souleyez/plugins/macos_ssh.py +3 -1
- souleyez/plugins/mdns.py +3 -1
- souleyez/plugins/msf_auxiliary.py +3 -2
- souleyez/plugins/msf_exploit.py +6 -5
- souleyez/plugins/nikto.py +5 -2
- souleyez/plugins/nmap.py +6 -4
- souleyez/plugins/nuclei.py +3 -1
- souleyez/plugins/nxc.py +1 -0
- souleyez/plugins/plugin_base.py +3 -2
- souleyez/plugins/plugin_template.py +3 -2
- souleyez/plugins/rdp_sec_check.py +1 -0
- souleyez/plugins/responder.py +2 -1
- souleyez/plugins/router_http_brute.py +3 -1
- souleyez/plugins/router_ssh_brute.py +3 -1
- souleyez/plugins/router_telnet_brute.py +3 -1
- souleyez/plugins/routersploit.py +5 -3
- souleyez/plugins/routersploit_exploit.py +5 -3
- souleyez/plugins/searchsploit.py +1 -0
- souleyez/plugins/service_explorer.py +2 -1
- souleyez/plugins/smbmap.py +3 -1
- souleyez/plugins/smbpasswd.py +1 -0
- souleyez/plugins/sqlmap.py +3 -1
- souleyez/plugins/theharvester.py +3 -1
- souleyez/plugins/tr069.py +3 -1
- souleyez/plugins/upnp.py +3 -1
- souleyez/plugins/upnp_abuse.py +4 -2
- souleyez/plugins/vnc_access.py +4 -2
- souleyez/plugins/vnc_brute.py +3 -1
- souleyez/plugins/web_login_test.py +1 -0
- souleyez/plugins/whois.py +3 -1
- souleyez/plugins/wpscan.py +3 -1
- souleyez/reporting/attack_chain.py +2 -1
- souleyez/reporting/charts.py +1 -0
- souleyez/reporting/compliance_mappings.py +1 -0
- souleyez/reporting/detection_report.py +10 -10
- souleyez/reporting/formatters.py +7 -12
- souleyez/reporting/generator.py +34 -46
- souleyez/reporting/metrics.py +2 -1
- souleyez/scanner.py +6 -3
- souleyez/security/__init__.py +7 -5
- souleyez/security/scope_validator.py +5 -4
- souleyez/security.py +5 -2
- souleyez/storage/credentials.py +14 -19
- souleyez/storage/crypto.py +7 -4
- souleyez/storage/database.py +6 -6
- souleyez/storage/db.py +8 -8
- souleyez/storage/deliverable_evidence.py +2 -1
- souleyez/storage/deliverable_exporter.py +3 -2
- souleyez/storage/deliverable_templates.py +2 -1
- souleyez/storage/deliverables.py +2 -1
- souleyez/storage/engagements.py +6 -4
- souleyez/storage/evidence.py +5 -4
- souleyez/storage/execution_log.py +4 -2
- souleyez/storage/exploit_attempts.py +3 -2
- souleyez/storage/exploits.py +3 -1
- souleyez/storage/findings.py +3 -1
- souleyez/storage/hosts.py +5 -2
- souleyez/storage/migrate_to_engagements.py +14 -24
- souleyez/storage/migrations/_001_add_credential_enhancements.py +12 -21
- souleyez/storage/migrations/_003_add_execution_log.py +8 -13
- souleyez/storage/migrations/_005_screenshots.py +2 -4
- souleyez/storage/migrations/_006_deliverables.py +2 -4
- souleyez/storage/migrations/_007_deliverable_templates.py +4 -8
- souleyez/storage/migrations/_008_add_nuclei_table.py +2 -4
- souleyez/storage/migrations/_010_evidence_linking.py +6 -12
- souleyez/storage/migrations/_012_team_collaboration.py +12 -24
- souleyez/storage/migrations/_013_add_host_tags.py +2 -4
- souleyez/storage/migrations/_014_exploit_attempts.py +10 -20
- souleyez/storage/migrations/_015_add_mac_os_fields.py +4 -8
- souleyez/storage/migrations/_016_add_domain_field.py +2 -4
- souleyez/storage/migrations/_017_msf_sessions.py +8 -16
- souleyez/storage/migrations/_018_add_osint_target.py +4 -8
- souleyez/storage/migrations/_019_add_engagement_type.py +4 -8
- souleyez/storage/migrations/_020_add_rbac.py +9 -17
- souleyez/storage/migrations/_021_wazuh_integration.py +4 -8
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +2 -4
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +4 -8
- souleyez/storage/migrations/_026_add_engagement_scope.py +4 -8
- souleyez/storage/migrations/_027_multi_siem_persistence.py +8 -16
- souleyez/storage/migrations/__init__.py +1 -4
- souleyez/storage/migrations/migration_manager.py +6 -9
- souleyez/storage/msf_sessions.py +1 -1
- souleyez/storage/osint.py +3 -1
- souleyez/storage/recommendation_engine.py +3 -2
- souleyez/storage/screenshots.py +2 -1
- souleyez/storage/smb_shares.py +3 -1
- souleyez/storage/sqlmap_data.py +6 -4
- souleyez/storage/team_collaboration.py +3 -2
- souleyez/storage/timeline_tracker.py +2 -1
- souleyez/storage/wazuh_vulns.py +3 -1
- souleyez/storage/web_paths.py +3 -1
- souleyez/testing/credential_tester.py +2 -0
- souleyez/ui/__init__.py +2 -1
- souleyez/ui/ai_quotes.py +1 -1
- souleyez/ui/attack_surface.py +50 -28
- souleyez/ui/chain_rules_view.py +6 -3
- souleyez/ui/correlation_view.py +3 -2
- souleyez/ui/dashboard.py +85 -139
- souleyez/ui/deliverables_view.py +1 -1
- souleyez/ui/design_system.py +5 -3
- souleyez/ui/errors.py +3 -1
- souleyez/ui/evidence_linking_view.py +2 -1
- souleyez/ui/evidence_vault.py +11 -6
- souleyez/ui/exploit_suggestions_view.py +11 -7
- souleyez/ui/export_view.py +3 -1
- souleyez/ui/gap_analysis_view.py +6 -3
- souleyez/ui/help_system.py +4 -1
- souleyez/ui/intelligence_view.py +7 -3
- souleyez/ui/interactive.py +1280 -558
- souleyez/ui/interactive_selector.py +3 -2
- souleyez/ui/log_formatter.py +1 -0
- souleyez/ui/menu_components.py +3 -1
- souleyez/ui/msf_auxiliary_menu.py +4 -1
- souleyez/ui/pending_chains_view.py +15 -12
- souleyez/ui/progress_indicators.py +5 -2
- souleyez/ui/recommendations_view.py +4 -2
- souleyez/ui/rule_builder.py +4 -1
- souleyez/ui/setup_wizard.py +10 -8
- souleyez/ui/shortcuts.py +1 -1
- souleyez/ui/splunk_gap_analysis_view.py +7 -4
- souleyez/ui/splunk_vulns_view.py +4 -1
- souleyez/ui/team_dashboard.py +7 -5
- souleyez/ui/template_selector.py +2 -1
- souleyez/ui/terminal.py +3 -2
- souleyez/ui/timeline_view.py +2 -1
- souleyez/ui/tool_setup.py +92 -31
- souleyez/ui/tutorial.py +7 -4
- souleyez/ui/tutorial_state.py +3 -2
- souleyez/ui/wazuh_vulns_view.py +5 -2
- souleyez/ui/wordlist_browser.py +4 -3
- souleyez/ui.py +13 -7
- souleyez/utils/tool_checker.py +95 -17
- souleyez/utils.py +4 -4
- souleyez/wordlists.py +1 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/METADATA +1 -1
- souleyez-3.0.9.dist-info/RECORD +445 -0
- souleyez-3.0.0.dist-info/RECORD +0 -443
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/WHEEL +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/entry_points.txt +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/licenses/LICENSE +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/top_level.txt +0 -0
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Migration script to rename workspaces to engagements
|
|
4
4
|
"""
|
|
5
|
+
|
|
5
6
|
import sqlite3
|
|
6
7
|
from pathlib import Path
|
|
8
|
+
|
|
7
9
|
from .. import config
|
|
8
10
|
|
|
9
11
|
DB_PATH = Path(config.get("database.path", "~/.souleyez/souleyez.db")).expanduser()
|
|
@@ -41,8 +43,7 @@ def migrate():
|
|
|
41
43
|
|
|
42
44
|
# Rename workspace_id to engagement_id in hosts table
|
|
43
45
|
print(" - Updating hosts table...")
|
|
44
|
-
cursor.execute(
|
|
45
|
-
"""
|
|
46
|
+
cursor.execute("""
|
|
46
47
|
CREATE TABLE hosts_new (
|
|
47
48
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
48
49
|
engagement_id INTEGER NOT NULL,
|
|
@@ -56,23 +57,19 @@ def migrate():
|
|
|
56
57
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
57
58
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
58
59
|
)
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
cursor.execute(
|
|
62
|
-
"""
|
|
60
|
+
""")
|
|
61
|
+
cursor.execute("""
|
|
63
62
|
INSERT INTO hosts_new
|
|
64
63
|
SELECT id, workspace_id, ip_address, hostname, os_name, os_accuracy,
|
|
65
64
|
mac_address, status, tags, created_at, updated_at
|
|
66
65
|
FROM hosts
|
|
67
|
-
"""
|
|
68
|
-
)
|
|
66
|
+
""")
|
|
69
67
|
cursor.execute("DROP TABLE hosts")
|
|
70
68
|
cursor.execute("ALTER TABLE hosts_new RENAME TO hosts")
|
|
71
69
|
|
|
72
70
|
# Rename workspace_id to engagement_id in findings table
|
|
73
71
|
print(" - Updating findings table...")
|
|
74
|
-
cursor.execute(
|
|
75
|
-
"""
|
|
72
|
+
cursor.execute("""
|
|
76
73
|
CREATE TABLE findings_new (
|
|
77
74
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
78
75
|
engagement_id INTEGER NOT NULL,
|
|
@@ -90,16 +87,13 @@ def migrate():
|
|
|
90
87
|
scan_id INTEGER,
|
|
91
88
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
92
89
|
)
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
cursor.execute(
|
|
96
|
-
"""
|
|
90
|
+
""")
|
|
91
|
+
cursor.execute("""
|
|
97
92
|
INSERT INTO findings_new
|
|
98
93
|
SELECT id, workspace_id, host_id, service_id, finding_type, severity,
|
|
99
94
|
title, description, evidence, refs, port, path, tool, scan_id, created_at
|
|
100
95
|
FROM findings
|
|
101
|
-
"""
|
|
102
|
-
)
|
|
96
|
+
""")
|
|
103
97
|
cursor.execute("DROP TABLE findings")
|
|
104
98
|
cursor.execute("ALTER TABLE findings_new RENAME TO findings")
|
|
105
99
|
|
|
@@ -109,8 +103,7 @@ def migrate():
|
|
|
109
103
|
)
|
|
110
104
|
if cursor.fetchone():
|
|
111
105
|
print(" - Updating osint_data table...")
|
|
112
|
-
cursor.execute(
|
|
113
|
-
"""
|
|
106
|
+
cursor.execute("""
|
|
114
107
|
CREATE TABLE osint_data_new (
|
|
115
108
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
116
109
|
engagement_id INTEGER NOT NULL,
|
|
@@ -119,15 +112,12 @@ def migrate():
|
|
|
119
112
|
source TEXT,
|
|
120
113
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
121
114
|
)
|
|
122
|
-
"""
|
|
123
|
-
|
|
124
|
-
cursor.execute(
|
|
125
|
-
"""
|
|
115
|
+
""")
|
|
116
|
+
cursor.execute("""
|
|
126
117
|
INSERT INTO osint_data_new
|
|
127
118
|
SELECT id, workspace_id, data_type, value, source, created_at
|
|
128
119
|
FROM osint_data
|
|
129
|
-
"""
|
|
130
|
-
)
|
|
120
|
+
""")
|
|
131
121
|
cursor.execute("DROP TABLE osint_data")
|
|
132
122
|
cursor.execute("ALTER TABLE osint_data_new RENAME TO osint_data")
|
|
133
123
|
|
|
@@ -4,8 +4,9 @@ Migration 001: Add credential enhancements
|
|
|
4
4
|
- Adds indices for better performance
|
|
5
5
|
- Adds created_at/updated_at tracking
|
|
6
6
|
"""
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
import os
|
|
9
|
+
import sqlite3
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def upgrade(conn: sqlite3.Connection):
|
|
@@ -18,30 +19,24 @@ def upgrade(conn: sqlite3.Connection):
|
|
|
18
19
|
# Add indices for credentials table
|
|
19
20
|
if not silent:
|
|
20
21
|
print(" → Creating index on credentials(engagement_id)")
|
|
21
|
-
cursor.execute(
|
|
22
|
-
"""
|
|
22
|
+
cursor.execute("""
|
|
23
23
|
CREATE INDEX IF NOT EXISTS idx_credentials_engagement
|
|
24
24
|
ON credentials(engagement_id)
|
|
25
|
-
"""
|
|
26
|
-
)
|
|
25
|
+
""")
|
|
27
26
|
|
|
28
27
|
if not silent:
|
|
29
28
|
print(" → Creating index on credentials(host_id)")
|
|
30
|
-
cursor.execute(
|
|
31
|
-
"""
|
|
29
|
+
cursor.execute("""
|
|
32
30
|
CREATE INDEX IF NOT EXISTS idx_credentials_host
|
|
33
31
|
ON credentials(host_id)
|
|
34
|
-
"""
|
|
35
|
-
)
|
|
32
|
+
""")
|
|
36
33
|
|
|
37
34
|
if not silent:
|
|
38
35
|
print(" → Creating index on credentials(status)")
|
|
39
|
-
cursor.execute(
|
|
40
|
-
"""
|
|
36
|
+
cursor.execute("""
|
|
41
37
|
CREATE INDEX IF NOT EXISTS idx_credentials_status
|
|
42
38
|
ON credentials(status)
|
|
43
|
-
"""
|
|
44
|
-
)
|
|
39
|
+
""")
|
|
45
40
|
|
|
46
41
|
# Add updated_at column if it doesn't exist
|
|
47
42
|
try:
|
|
@@ -49,20 +44,16 @@ def upgrade(conn: sqlite3.Connection):
|
|
|
49
44
|
except sqlite3.OperationalError:
|
|
50
45
|
if not silent:
|
|
51
46
|
print(" → Adding updated_at column to credentials table")
|
|
52
|
-
cursor.execute(
|
|
53
|
-
"""
|
|
47
|
+
cursor.execute("""
|
|
54
48
|
ALTER TABLE credentials
|
|
55
49
|
ADD COLUMN updated_at TIMESTAMP
|
|
56
|
-
"""
|
|
57
|
-
)
|
|
50
|
+
""")
|
|
58
51
|
# Set default value for existing rows
|
|
59
|
-
cursor.execute(
|
|
60
|
-
"""
|
|
52
|
+
cursor.execute("""
|
|
61
53
|
UPDATE credentials
|
|
62
54
|
SET updated_at = CURRENT_TIMESTAMP
|
|
63
55
|
WHERE updated_at IS NULL
|
|
64
|
-
"""
|
|
65
|
-
)
|
|
56
|
+
""")
|
|
66
57
|
|
|
67
58
|
conn.commit()
|
|
68
59
|
if not silent:
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Migration 003: Add execution_log table for tracking AI-driven executions
|
|
4
4
|
"""
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
import os
|
|
7
|
+
import sqlite3
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def upgrade(conn):
|
|
@@ -14,8 +15,7 @@ def upgrade(conn):
|
|
|
14
15
|
silent = os.environ.get("SOULEYEZ_MIGRATION_SILENT", "0") == "1"
|
|
15
16
|
|
|
16
17
|
# Create execution_log table
|
|
17
|
-
cursor.execute(
|
|
18
|
-
"""
|
|
18
|
+
cursor.execute("""
|
|
19
19
|
CREATE TABLE IF NOT EXISTS execution_log (
|
|
20
20
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
21
21
|
engagement_id INTEGER NOT NULL,
|
|
@@ -32,23 +32,18 @@ def upgrade(conn):
|
|
|
32
32
|
feedback_applied TEXT,
|
|
33
33
|
FOREIGN KEY (engagement_id) REFERENCES engagements(id)
|
|
34
34
|
)
|
|
35
|
-
"""
|
|
36
|
-
)
|
|
35
|
+
""")
|
|
37
36
|
|
|
38
37
|
# Create index for faster queries
|
|
39
|
-
cursor.execute(
|
|
40
|
-
"""
|
|
38
|
+
cursor.execute("""
|
|
41
39
|
CREATE INDEX IF NOT EXISTS idx_execution_engagement
|
|
42
40
|
ON execution_log(engagement_id)
|
|
43
|
-
"""
|
|
44
|
-
)
|
|
41
|
+
""")
|
|
45
42
|
|
|
46
|
-
cursor.execute(
|
|
47
|
-
"""
|
|
43
|
+
cursor.execute("""
|
|
48
44
|
CREATE INDEX IF NOT EXISTS idx_execution_timestamp
|
|
49
45
|
ON execution_log(executed_at DESC)
|
|
50
|
-
"""
|
|
51
|
-
)
|
|
46
|
+
""")
|
|
52
47
|
|
|
53
48
|
conn.commit()
|
|
54
49
|
if not silent:
|
|
@@ -5,8 +5,7 @@ Migration 005: Add screenshots table for visual evidence management.
|
|
|
5
5
|
|
|
6
6
|
def upgrade(db):
|
|
7
7
|
"""Add screenshots table."""
|
|
8
|
-
db.execute(
|
|
9
|
-
"""
|
|
8
|
+
db.execute("""
|
|
10
9
|
CREATE TABLE IF NOT EXISTS screenshots (
|
|
11
10
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
12
11
|
engagement_id INTEGER NOT NULL,
|
|
@@ -23,8 +22,7 @@ def upgrade(db):
|
|
|
23
22
|
FOREIGN KEY (host_id) REFERENCES hosts(id),
|
|
24
23
|
FOREIGN KEY (finding_id) REFERENCES findings(id)
|
|
25
24
|
)
|
|
26
|
-
"""
|
|
27
|
-
)
|
|
25
|
+
""")
|
|
28
26
|
|
|
29
27
|
db.execute(
|
|
30
28
|
"CREATE INDEX IF NOT EXISTS idx_screenshots_engagement ON screenshots(engagement_id)"
|
|
@@ -5,8 +5,7 @@ Migration 006: Add deliverables tracking table.
|
|
|
5
5
|
|
|
6
6
|
def upgrade(db):
|
|
7
7
|
"""Add deliverables tracking table."""
|
|
8
|
-
db.execute(
|
|
9
|
-
"""
|
|
8
|
+
db.execute("""
|
|
10
9
|
CREATE TABLE IF NOT EXISTS deliverables (
|
|
11
10
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
12
11
|
engagement_id INTEGER NOT NULL,
|
|
@@ -24,8 +23,7 @@ def upgrade(db):
|
|
|
24
23
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
25
24
|
FOREIGN KEY (engagement_id) REFERENCES engagements(id)
|
|
26
25
|
)
|
|
27
|
-
"""
|
|
28
|
-
)
|
|
26
|
+
""")
|
|
29
27
|
|
|
30
28
|
db.execute(
|
|
31
29
|
"CREATE INDEX IF NOT EXISTS idx_deliverables_engagement ON deliverables(engagement_id)"
|
|
@@ -7,8 +7,7 @@ def upgrade(conn):
|
|
|
7
7
|
"""Add deliverable templates table."""
|
|
8
8
|
|
|
9
9
|
# Templates table
|
|
10
|
-
conn.execute(
|
|
11
|
-
"""
|
|
10
|
+
conn.execute("""
|
|
12
11
|
CREATE TABLE IF NOT EXISTS deliverable_templates (
|
|
13
12
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
14
13
|
name TEXT NOT NULL,
|
|
@@ -20,16 +19,13 @@ def upgrade(conn):
|
|
|
20
19
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
21
20
|
is_builtin INTEGER DEFAULT 0
|
|
22
21
|
)
|
|
23
|
-
"""
|
|
24
|
-
)
|
|
22
|
+
""")
|
|
25
23
|
|
|
26
24
|
# Index for faster lookups
|
|
27
|
-
conn.execute(
|
|
28
|
-
"""
|
|
25
|
+
conn.execute("""
|
|
29
26
|
CREATE INDEX IF NOT EXISTS idx_templates_framework
|
|
30
27
|
ON deliverable_templates(framework, engagement_type)
|
|
31
|
-
"""
|
|
32
|
-
)
|
|
28
|
+
""")
|
|
33
29
|
|
|
34
30
|
print("✅ Migration 007: Deliverable templates table created")
|
|
35
31
|
|
|
@@ -5,8 +5,7 @@ Migration 008: Add nuclei findings table for vulnerability scan results.
|
|
|
5
5
|
|
|
6
6
|
def upgrade(db):
|
|
7
7
|
"""Add nuclei_findings table."""
|
|
8
|
-
db.execute(
|
|
9
|
-
"""
|
|
8
|
+
db.execute("""
|
|
10
9
|
CREATE TABLE IF NOT EXISTS nuclei_findings (
|
|
11
10
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
12
11
|
engagement_id INTEGER NOT NULL,
|
|
@@ -25,8 +24,7 @@ def upgrade(db):
|
|
|
25
24
|
found_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
26
25
|
FOREIGN KEY(engagement_id) REFERENCES engagements(id) ON DELETE CASCADE
|
|
27
26
|
)
|
|
28
|
-
"""
|
|
29
|
-
)
|
|
27
|
+
""")
|
|
30
28
|
|
|
31
29
|
db.execute(
|
|
32
30
|
"CREATE INDEX IF NOT EXISTS idx_nuclei_engagement ON nuclei_findings(engagement_id)"
|
|
@@ -6,8 +6,7 @@ Migration 010: Evidence linking system for deliverables
|
|
|
6
6
|
def upgrade(conn):
|
|
7
7
|
"""Add evidence linking table."""
|
|
8
8
|
|
|
9
|
-
conn.execute(
|
|
10
|
-
"""
|
|
9
|
+
conn.execute("""
|
|
11
10
|
CREATE TABLE IF NOT EXISTS deliverable_evidence (
|
|
12
11
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
13
12
|
deliverable_id INTEGER NOT NULL,
|
|
@@ -18,22 +17,17 @@ def upgrade(conn):
|
|
|
18
17
|
notes TEXT,
|
|
19
18
|
FOREIGN KEY (deliverable_id) REFERENCES deliverables(id) ON DELETE CASCADE
|
|
20
19
|
)
|
|
21
|
-
"""
|
|
22
|
-
)
|
|
20
|
+
""")
|
|
23
21
|
|
|
24
|
-
conn.execute(
|
|
25
|
-
"""
|
|
22
|
+
conn.execute("""
|
|
26
23
|
CREATE INDEX IF NOT EXISTS idx_deliverable_evidence
|
|
27
24
|
ON deliverable_evidence(deliverable_id, evidence_type)
|
|
28
|
-
"""
|
|
29
|
-
)
|
|
25
|
+
""")
|
|
30
26
|
|
|
31
|
-
conn.execute(
|
|
32
|
-
"""
|
|
27
|
+
conn.execute("""
|
|
33
28
|
CREATE INDEX IF NOT EXISTS idx_evidence_lookup
|
|
34
29
|
ON deliverable_evidence(evidence_type, evidence_id)
|
|
35
|
-
"""
|
|
36
|
-
)
|
|
30
|
+
""")
|
|
37
31
|
|
|
38
32
|
print("✅ Migration 010: Evidence linking system created")
|
|
39
33
|
|
|
@@ -18,8 +18,7 @@ def upgrade(conn):
|
|
|
18
18
|
pass # Column may already exist
|
|
19
19
|
|
|
20
20
|
# Activity log for tracking who did what
|
|
21
|
-
conn.execute(
|
|
22
|
-
"""
|
|
21
|
+
conn.execute("""
|
|
23
22
|
CREATE TABLE IF NOT EXISTS deliverable_activity (
|
|
24
23
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
25
24
|
deliverable_id INTEGER NOT NULL,
|
|
@@ -31,34 +30,26 @@ def upgrade(conn):
|
|
|
31
30
|
FOREIGN KEY (deliverable_id) REFERENCES deliverables(id) ON DELETE CASCADE,
|
|
32
31
|
FOREIGN KEY (engagement_id) REFERENCES engagements(id) ON DELETE CASCADE
|
|
33
32
|
)
|
|
34
|
-
"""
|
|
35
|
-
)
|
|
33
|
+
""")
|
|
36
34
|
|
|
37
35
|
# Index for faster activity queries
|
|
38
|
-
conn.execute(
|
|
39
|
-
"""
|
|
36
|
+
conn.execute("""
|
|
40
37
|
CREATE INDEX IF NOT EXISTS idx_activity_deliverable
|
|
41
38
|
ON deliverable_activity(deliverable_id, created_at DESC)
|
|
42
|
-
"""
|
|
43
|
-
)
|
|
39
|
+
""")
|
|
44
40
|
|
|
45
|
-
conn.execute(
|
|
46
|
-
"""
|
|
41
|
+
conn.execute("""
|
|
47
42
|
CREATE INDEX IF NOT EXISTS idx_activity_engagement
|
|
48
43
|
ON deliverable_activity(engagement_id, created_at DESC)
|
|
49
|
-
"""
|
|
50
|
-
)
|
|
44
|
+
""")
|
|
51
45
|
|
|
52
|
-
conn.execute(
|
|
53
|
-
"""
|
|
46
|
+
conn.execute("""
|
|
54
47
|
CREATE INDEX IF NOT EXISTS idx_activity_user
|
|
55
48
|
ON deliverable_activity(user, created_at DESC)
|
|
56
|
-
"""
|
|
57
|
-
)
|
|
49
|
+
""")
|
|
58
50
|
|
|
59
51
|
# Comments on deliverables
|
|
60
|
-
conn.execute(
|
|
61
|
-
"""
|
|
52
|
+
conn.execute("""
|
|
62
53
|
CREATE TABLE IF NOT EXISTS deliverable_comments (
|
|
63
54
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
64
55
|
deliverable_id INTEGER NOT NULL,
|
|
@@ -67,15 +58,12 @@ def upgrade(conn):
|
|
|
67
58
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
68
59
|
FOREIGN KEY (deliverable_id) REFERENCES deliverables(id) ON DELETE CASCADE
|
|
69
60
|
)
|
|
70
|
-
"""
|
|
71
|
-
)
|
|
61
|
+
""")
|
|
72
62
|
|
|
73
|
-
conn.execute(
|
|
74
|
-
"""
|
|
63
|
+
conn.execute("""
|
|
75
64
|
CREATE INDEX IF NOT EXISTS idx_comments_deliverable
|
|
76
65
|
ON deliverable_comments(deliverable_id, created_at DESC)
|
|
77
|
-
"""
|
|
78
|
-
)
|
|
66
|
+
""")
|
|
79
67
|
|
|
80
68
|
print("✅ Migration 012: Team collaboration tables created")
|
|
81
69
|
|
|
@@ -13,12 +13,10 @@ def upgrade(db):
|
|
|
13
13
|
columns = [row[1] for row in cursor.fetchall()]
|
|
14
14
|
|
|
15
15
|
if "tags" not in columns:
|
|
16
|
-
db.execute(
|
|
17
|
-
"""
|
|
16
|
+
db.execute("""
|
|
18
17
|
ALTER TABLE hosts
|
|
19
18
|
ADD COLUMN tags TEXT DEFAULT NULL
|
|
20
|
-
"""
|
|
21
|
-
)
|
|
19
|
+
""")
|
|
22
20
|
# If column already exists, migration is idempotent - do nothing
|
|
23
21
|
|
|
24
22
|
|
|
@@ -9,8 +9,7 @@ and provides a historical record of exploitation activities.
|
|
|
9
9
|
|
|
10
10
|
def upgrade(db):
|
|
11
11
|
"""Create exploit_attempts table."""
|
|
12
|
-
db.execute(
|
|
13
|
-
"""
|
|
12
|
+
db.execute("""
|
|
14
13
|
CREATE TABLE IF NOT EXISTS exploit_attempts (
|
|
15
14
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
15
|
engagement_id INTEGER NOT NULL,
|
|
@@ -28,37 +27,28 @@ def upgrade(db):
|
|
|
28
27
|
FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE SET NULL,
|
|
29
28
|
UNIQUE(engagement_id, host_id, service_id, exploit_identifier)
|
|
30
29
|
)
|
|
31
|
-
"""
|
|
32
|
-
)
|
|
30
|
+
""")
|
|
33
31
|
|
|
34
32
|
# Create indexes for efficient querying
|
|
35
|
-
db.execute(
|
|
36
|
-
"""
|
|
33
|
+
db.execute("""
|
|
37
34
|
CREATE INDEX IF NOT EXISTS idx_exploit_attempts_engagement
|
|
38
35
|
ON exploit_attempts(engagement_id)
|
|
39
|
-
"""
|
|
40
|
-
)
|
|
36
|
+
""")
|
|
41
37
|
|
|
42
|
-
db.execute(
|
|
43
|
-
"""
|
|
38
|
+
db.execute("""
|
|
44
39
|
CREATE INDEX IF NOT EXISTS idx_exploit_attempts_host
|
|
45
40
|
ON exploit_attempts(host_id)
|
|
46
|
-
"""
|
|
47
|
-
)
|
|
41
|
+
""")
|
|
48
42
|
|
|
49
|
-
db.execute(
|
|
50
|
-
"""
|
|
43
|
+
db.execute("""
|
|
51
44
|
CREATE INDEX IF NOT EXISTS idx_exploit_attempts_status
|
|
52
45
|
ON exploit_attempts(status)
|
|
53
|
-
"""
|
|
54
|
-
)
|
|
46
|
+
""")
|
|
55
47
|
|
|
56
|
-
db.execute(
|
|
57
|
-
"""
|
|
48
|
+
db.execute("""
|
|
58
49
|
CREATE INDEX IF NOT EXISTS idx_exploit_attempts_identifier
|
|
59
50
|
ON exploit_attempts(exploit_identifier)
|
|
60
|
-
"""
|
|
61
|
-
)
|
|
51
|
+
""")
|
|
62
52
|
|
|
63
53
|
|
|
64
54
|
def downgrade(db):
|
|
@@ -13,20 +13,16 @@ def upgrade(db):
|
|
|
13
13
|
columns = [row[1] for row in cursor.fetchall()]
|
|
14
14
|
|
|
15
15
|
if "mac_address" not in columns:
|
|
16
|
-
db.execute(
|
|
17
|
-
"""
|
|
16
|
+
db.execute("""
|
|
18
17
|
ALTER TABLE hosts
|
|
19
18
|
ADD COLUMN mac_address TEXT DEFAULT NULL
|
|
20
|
-
"""
|
|
21
|
-
)
|
|
19
|
+
""")
|
|
22
20
|
|
|
23
21
|
if "os_accuracy" not in columns:
|
|
24
|
-
db.execute(
|
|
25
|
-
"""
|
|
22
|
+
db.execute("""
|
|
26
23
|
ALTER TABLE hosts
|
|
27
24
|
ADD COLUMN os_accuracy INTEGER DEFAULT NULL
|
|
28
|
-
"""
|
|
29
|
-
)
|
|
25
|
+
""")
|
|
30
26
|
|
|
31
27
|
|
|
32
28
|
def downgrade(db):
|
|
@@ -14,12 +14,10 @@ def upgrade(db):
|
|
|
14
14
|
columns = [row[1] for row in cursor.fetchall()]
|
|
15
15
|
|
|
16
16
|
if "domain" not in columns:
|
|
17
|
-
db.execute(
|
|
18
|
-
"""
|
|
17
|
+
db.execute("""
|
|
19
18
|
ALTER TABLE hosts
|
|
20
19
|
ADD COLUMN domain TEXT DEFAULT NULL
|
|
21
|
-
"""
|
|
22
|
-
)
|
|
20
|
+
""")
|
|
23
21
|
|
|
24
22
|
|
|
25
23
|
def downgrade(db):
|
|
@@ -13,8 +13,7 @@ def upgrade(conn):
|
|
|
13
13
|
cursor = conn.cursor()
|
|
14
14
|
|
|
15
15
|
# Create msf_sessions table
|
|
16
|
-
cursor.execute(
|
|
17
|
-
"""
|
|
16
|
+
cursor.execute("""
|
|
18
17
|
CREATE TABLE IF NOT EXISTS msf_sessions (
|
|
19
18
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
19
|
engagement_id INTEGER NOT NULL,
|
|
@@ -40,30 +39,23 @@ def upgrade(conn):
|
|
|
40
39
|
FOREIGN KEY (host_id) REFERENCES hosts(id) ON DELETE CASCADE,
|
|
41
40
|
UNIQUE(engagement_id, msf_session_id)
|
|
42
41
|
)
|
|
43
|
-
"""
|
|
44
|
-
)
|
|
42
|
+
""")
|
|
45
43
|
|
|
46
44
|
# Create index for faster lookups
|
|
47
|
-
cursor.execute(
|
|
48
|
-
"""
|
|
45
|
+
cursor.execute("""
|
|
49
46
|
CREATE INDEX IF NOT EXISTS idx_msf_sessions_engagement
|
|
50
47
|
ON msf_sessions(engagement_id)
|
|
51
|
-
"""
|
|
52
|
-
)
|
|
48
|
+
""")
|
|
53
49
|
|
|
54
|
-
cursor.execute(
|
|
55
|
-
"""
|
|
50
|
+
cursor.execute("""
|
|
56
51
|
CREATE INDEX IF NOT EXISTS idx_msf_sessions_host
|
|
57
52
|
ON msf_sessions(host_id)
|
|
58
|
-
"""
|
|
59
|
-
)
|
|
53
|
+
""")
|
|
60
54
|
|
|
61
|
-
cursor.execute(
|
|
62
|
-
"""
|
|
55
|
+
cursor.execute("""
|
|
63
56
|
CREATE INDEX IF NOT EXISTS idx_msf_sessions_active
|
|
64
57
|
ON msf_sessions(is_active)
|
|
65
|
-
"""
|
|
66
|
-
)
|
|
58
|
+
""")
|
|
67
59
|
|
|
68
60
|
conn.commit()
|
|
69
61
|
|
|
@@ -18,19 +18,15 @@ def upgrade(conn):
|
|
|
18
18
|
|
|
19
19
|
# Add target column only if it doesn't exist
|
|
20
20
|
if "target" not in columns:
|
|
21
|
-
cursor.execute(
|
|
22
|
-
"""
|
|
21
|
+
cursor.execute("""
|
|
23
22
|
ALTER TABLE osint_data ADD COLUMN target TEXT
|
|
24
|
-
"""
|
|
25
|
-
)
|
|
23
|
+
""")
|
|
26
24
|
|
|
27
25
|
# Create index for faster lookups by target
|
|
28
|
-
cursor.execute(
|
|
29
|
-
"""
|
|
26
|
+
cursor.execute("""
|
|
30
27
|
CREATE INDEX IF NOT EXISTS idx_osint_target
|
|
31
28
|
ON osint_data(target)
|
|
32
|
-
"""
|
|
33
|
-
)
|
|
29
|
+
""")
|
|
34
30
|
|
|
35
31
|
conn.commit()
|
|
36
32
|
|
|
@@ -18,19 +18,15 @@ def upgrade(conn):
|
|
|
18
18
|
|
|
19
19
|
# Add engagement_type column only if it doesn't exist
|
|
20
20
|
if "engagement_type" not in columns:
|
|
21
|
-
cursor.execute(
|
|
22
|
-
"""
|
|
21
|
+
cursor.execute("""
|
|
23
22
|
ALTER TABLE engagements ADD COLUMN engagement_type TEXT DEFAULT 'custom'
|
|
24
|
-
"""
|
|
25
|
-
)
|
|
23
|
+
""")
|
|
26
24
|
|
|
27
25
|
# Create index for faster lookups by type
|
|
28
|
-
cursor.execute(
|
|
29
|
-
"""
|
|
26
|
+
cursor.execute("""
|
|
30
27
|
CREATE INDEX IF NOT EXISTS idx_engagements_type
|
|
31
28
|
ON engagements(engagement_type)
|
|
32
|
-
"""
|
|
33
|
-
)
|
|
29
|
+
""")
|
|
34
30
|
|
|
35
31
|
conn.commit()
|
|
36
32
|
|