souleyez 2.43.29__py3-none-any.whl → 2.43.34__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 +9526 -2879
- 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 +563 -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 +408 -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 +371 -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 +854 -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 +173 -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 +223 -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 +22827 -10678
- 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-2.43.34.dist-info}/METADATA +1 -1
- souleyez-2.43.34.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ import sqlite3
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from .. import config
|
|
8
8
|
|
|
9
|
-
DB_PATH = Path(config.get(
|
|
9
|
+
DB_PATH = Path(config.get("database.path", "~/.souleyez/souleyez.db")).expanduser()
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def migrate():
|
|
@@ -21,7 +21,9 @@ def migrate():
|
|
|
21
21
|
cursor = conn.cursor()
|
|
22
22
|
|
|
23
23
|
# Check if workspaces table exists
|
|
24
|
-
cursor.execute(
|
|
24
|
+
cursor.execute(
|
|
25
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='workspaces'"
|
|
26
|
+
)
|
|
25
27
|
if not cursor.fetchone():
|
|
26
28
|
print("Database already migrated or no workspaces table found.")
|
|
27
29
|
conn.close()
|
|
@@ -39,7 +41,8 @@ def migrate():
|
|
|
39
41
|
|
|
40
42
|
# Rename workspace_id to engagement_id in hosts table
|
|
41
43
|
print(" - Updating hosts table...")
|
|
42
|
-
cursor.execute(
|
|
44
|
+
cursor.execute(
|
|
45
|
+
"""
|
|
43
46
|
CREATE TABLE hosts_new (
|
|
44
47
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
45
48
|
engagement_id INTEGER NOT NULL,
|
|
@@ -53,19 +56,23 @@ def migrate():
|
|
|
53
56
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
54
57
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
55
58
|
)
|
|
56
|
-
"""
|
|
57
|
-
|
|
59
|
+
"""
|
|
60
|
+
)
|
|
61
|
+
cursor.execute(
|
|
62
|
+
"""
|
|
58
63
|
INSERT INTO hosts_new
|
|
59
64
|
SELECT id, workspace_id, ip_address, hostname, os_name, os_accuracy,
|
|
60
65
|
mac_address, status, tags, created_at, updated_at
|
|
61
66
|
FROM hosts
|
|
62
|
-
"""
|
|
67
|
+
"""
|
|
68
|
+
)
|
|
63
69
|
cursor.execute("DROP TABLE hosts")
|
|
64
70
|
cursor.execute("ALTER TABLE hosts_new RENAME TO hosts")
|
|
65
71
|
|
|
66
72
|
# Rename workspace_id to engagement_id in findings table
|
|
67
73
|
print(" - Updating findings table...")
|
|
68
|
-
cursor.execute(
|
|
74
|
+
cursor.execute(
|
|
75
|
+
"""
|
|
69
76
|
CREATE TABLE findings_new (
|
|
70
77
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
71
78
|
engagement_id INTEGER NOT NULL,
|
|
@@ -83,21 +90,27 @@ def migrate():
|
|
|
83
90
|
scan_id INTEGER,
|
|
84
91
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
85
92
|
)
|
|
86
|
-
"""
|
|
87
|
-
|
|
93
|
+
"""
|
|
94
|
+
)
|
|
95
|
+
cursor.execute(
|
|
96
|
+
"""
|
|
88
97
|
INSERT INTO findings_new
|
|
89
98
|
SELECT id, workspace_id, host_id, service_id, finding_type, severity,
|
|
90
99
|
title, description, evidence, refs, port, path, tool, scan_id, created_at
|
|
91
100
|
FROM findings
|
|
92
|
-
"""
|
|
101
|
+
"""
|
|
102
|
+
)
|
|
93
103
|
cursor.execute("DROP TABLE findings")
|
|
94
104
|
cursor.execute("ALTER TABLE findings_new RENAME TO findings")
|
|
95
105
|
|
|
96
106
|
# Check if osint_data table exists and update it
|
|
97
|
-
cursor.execute(
|
|
107
|
+
cursor.execute(
|
|
108
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='osint_data'"
|
|
109
|
+
)
|
|
98
110
|
if cursor.fetchone():
|
|
99
111
|
print(" - Updating osint_data table...")
|
|
100
|
-
cursor.execute(
|
|
112
|
+
cursor.execute(
|
|
113
|
+
"""
|
|
101
114
|
CREATE TABLE osint_data_new (
|
|
102
115
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
103
116
|
engagement_id INTEGER NOT NULL,
|
|
@@ -106,12 +119,15 @@ def migrate():
|
|
|
106
119
|
source TEXT,
|
|
107
120
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
108
121
|
)
|
|
109
|
-
"""
|
|
110
|
-
|
|
122
|
+
"""
|
|
123
|
+
)
|
|
124
|
+
cursor.execute(
|
|
125
|
+
"""
|
|
111
126
|
INSERT INTO osint_data_new
|
|
112
127
|
SELECT id, workspace_id, data_type, value, source, created_at
|
|
113
128
|
FROM osint_data
|
|
114
|
-
"""
|
|
129
|
+
"""
|
|
130
|
+
)
|
|
115
131
|
cursor.execute("DROP TABLE osint_data")
|
|
116
132
|
cursor.execute("ALTER TABLE osint_data_new RENAME TO osint_data")
|
|
117
133
|
|
|
@@ -119,11 +135,19 @@ def migrate():
|
|
|
119
135
|
print(" - Recreating indexes...")
|
|
120
136
|
cursor.execute("DROP INDEX IF EXISTS idx_hosts_workspace")
|
|
121
137
|
cursor.execute("DROP INDEX IF EXISTS idx_findings_workspace")
|
|
122
|
-
cursor.execute(
|
|
138
|
+
cursor.execute(
|
|
139
|
+
"CREATE INDEX IF NOT EXISTS idx_hosts_engagement ON hosts(engagement_id)"
|
|
140
|
+
)
|
|
123
141
|
cursor.execute("CREATE INDEX IF NOT EXISTS idx_hosts_ip ON hosts(ip_address)")
|
|
124
|
-
cursor.execute(
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
cursor.execute(
|
|
143
|
+
"CREATE INDEX IF NOT EXISTS idx_services_host ON services(host_id)"
|
|
144
|
+
)
|
|
145
|
+
cursor.execute(
|
|
146
|
+
"CREATE INDEX IF NOT EXISTS idx_findings_engagement ON findings(engagement_id)"
|
|
147
|
+
)
|
|
148
|
+
cursor.execute(
|
|
149
|
+
"CREATE INDEX IF NOT EXISTS idx_findings_severity ON findings(severity)"
|
|
150
|
+
)
|
|
127
151
|
|
|
128
152
|
# Update current_workspace file to current_engagement
|
|
129
153
|
old_file = Path.home() / ".souleyez" / "current_workspace"
|
|
@@ -11,31 +11,37 @@ import os
|
|
|
11
11
|
def upgrade(conn: sqlite3.Connection):
|
|
12
12
|
"""Apply migration."""
|
|
13
13
|
cursor = conn.cursor()
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
# Check if we should suppress output (silent mode)
|
|
16
|
-
silent = os.environ.get(
|
|
16
|
+
silent = os.environ.get("SOULEYEZ_MIGRATION_SILENT", "0") == "1"
|
|
17
17
|
|
|
18
18
|
# Add indices for credentials table
|
|
19
19
|
if not silent:
|
|
20
20
|
print(" → Creating index on credentials(engagement_id)")
|
|
21
|
-
cursor.execute(
|
|
21
|
+
cursor.execute(
|
|
22
|
+
"""
|
|
22
23
|
CREATE INDEX IF NOT EXISTS idx_credentials_engagement
|
|
23
24
|
ON credentials(engagement_id)
|
|
24
|
-
"""
|
|
25
|
+
"""
|
|
26
|
+
)
|
|
25
27
|
|
|
26
28
|
if not silent:
|
|
27
29
|
print(" → Creating index on credentials(host_id)")
|
|
28
|
-
cursor.execute(
|
|
30
|
+
cursor.execute(
|
|
31
|
+
"""
|
|
29
32
|
CREATE INDEX IF NOT EXISTS idx_credentials_host
|
|
30
33
|
ON credentials(host_id)
|
|
31
|
-
"""
|
|
34
|
+
"""
|
|
35
|
+
)
|
|
32
36
|
|
|
33
37
|
if not silent:
|
|
34
38
|
print(" → Creating index on credentials(status)")
|
|
35
|
-
cursor.execute(
|
|
39
|
+
cursor.execute(
|
|
40
|
+
"""
|
|
36
41
|
CREATE INDEX IF NOT EXISTS idx_credentials_status
|
|
37
42
|
ON credentials(status)
|
|
38
|
-
"""
|
|
43
|
+
"""
|
|
44
|
+
)
|
|
39
45
|
|
|
40
46
|
# Add updated_at column if it doesn't exist
|
|
41
47
|
try:
|
|
@@ -43,16 +49,20 @@ def upgrade(conn: sqlite3.Connection):
|
|
|
43
49
|
except sqlite3.OperationalError:
|
|
44
50
|
if not silent:
|
|
45
51
|
print(" → Adding updated_at column to credentials table")
|
|
46
|
-
cursor.execute(
|
|
52
|
+
cursor.execute(
|
|
53
|
+
"""
|
|
47
54
|
ALTER TABLE credentials
|
|
48
55
|
ADD COLUMN updated_at TIMESTAMP
|
|
49
|
-
"""
|
|
56
|
+
"""
|
|
57
|
+
)
|
|
50
58
|
# Set default value for existing rows
|
|
51
|
-
cursor.execute(
|
|
59
|
+
cursor.execute(
|
|
60
|
+
"""
|
|
52
61
|
UPDATE credentials
|
|
53
62
|
SET updated_at = CURRENT_TIMESTAMP
|
|
54
63
|
WHERE updated_at IS NULL
|
|
55
|
-
"""
|
|
64
|
+
"""
|
|
65
|
+
)
|
|
56
66
|
|
|
57
67
|
conn.commit()
|
|
58
68
|
if not silent:
|
|
@@ -6,22 +6,23 @@ Migration 002: Add status tracking fields to hosts and credentials tables
|
|
|
6
6
|
|
|
7
7
|
def upgrade(conn):
|
|
8
8
|
"""Add notes and tracking fields."""
|
|
9
|
+
|
|
9
10
|
# Helper to check if column exists
|
|
10
11
|
def column_exists(table, column):
|
|
11
12
|
cursor = conn.execute(f"PRAGMA table_info({table})")
|
|
12
13
|
columns = [row[1] for row in cursor.fetchall()]
|
|
13
14
|
return column in columns
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
# Add columns to credentials table if they don't exist
|
|
16
|
-
if not column_exists(
|
|
17
|
+
if not column_exists("credentials", "notes"):
|
|
17
18
|
conn.execute("ALTER TABLE credentials ADD COLUMN notes TEXT")
|
|
18
|
-
if not column_exists(
|
|
19
|
+
if not column_exists("credentials", "last_tested"):
|
|
19
20
|
conn.execute("ALTER TABLE credentials ADD COLUMN last_tested TIMESTAMP")
|
|
20
|
-
|
|
21
|
+
|
|
21
22
|
# Add columns to hosts table if they don't exist
|
|
22
|
-
if not column_exists(
|
|
23
|
+
if not column_exists("hosts", "access_level"):
|
|
23
24
|
conn.execute("ALTER TABLE hosts ADD COLUMN access_level TEXT DEFAULT 'none'")
|
|
24
|
-
if not column_exists(
|
|
25
|
+
if not column_exists("hosts", "notes"):
|
|
25
26
|
conn.execute("ALTER TABLE hosts ADD COLUMN notes TEXT")
|
|
26
27
|
|
|
27
28
|
|
|
@@ -30,4 +31,6 @@ def downgrade(conn):
|
|
|
30
31
|
SQLite doesn't support DROP COLUMN easily.
|
|
31
32
|
Would need to recreate tables without these columns.
|
|
32
33
|
"""
|
|
33
|
-
raise NotImplementedError(
|
|
34
|
+
raise NotImplementedError(
|
|
35
|
+
"Downgrade not supported for SQLite ALTER TABLE ADD COLUMN"
|
|
36
|
+
)
|
|
@@ -9,12 +9,13 @@ import os
|
|
|
9
9
|
def upgrade(conn):
|
|
10
10
|
"""Apply migration - add execution_log table."""
|
|
11
11
|
cursor = conn.cursor()
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
# Check if we should suppress output (silent mode)
|
|
14
|
-
silent = os.environ.get(
|
|
14
|
+
silent = os.environ.get("SOULEYEZ_MIGRATION_SILENT", "0") == "1"
|
|
15
15
|
|
|
16
16
|
# Create execution_log table
|
|
17
|
-
cursor.execute(
|
|
17
|
+
cursor.execute(
|
|
18
|
+
"""
|
|
18
19
|
CREATE TABLE IF NOT EXISTS execution_log (
|
|
19
20
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
21
|
engagement_id INTEGER NOT NULL,
|
|
@@ -31,18 +32,23 @@ def upgrade(conn):
|
|
|
31
32
|
feedback_applied TEXT,
|
|
32
33
|
FOREIGN KEY (engagement_id) REFERENCES engagements(id)
|
|
33
34
|
)
|
|
34
|
-
"""
|
|
35
|
+
"""
|
|
36
|
+
)
|
|
35
37
|
|
|
36
38
|
# Create index for faster queries
|
|
37
|
-
cursor.execute(
|
|
39
|
+
cursor.execute(
|
|
40
|
+
"""
|
|
38
41
|
CREATE INDEX IF NOT EXISTS idx_execution_engagement
|
|
39
42
|
ON execution_log(engagement_id)
|
|
40
|
-
"""
|
|
43
|
+
"""
|
|
44
|
+
)
|
|
41
45
|
|
|
42
|
-
cursor.execute(
|
|
46
|
+
cursor.execute(
|
|
47
|
+
"""
|
|
43
48
|
CREATE INDEX IF NOT EXISTS idx_execution_timestamp
|
|
44
49
|
ON execution_log(executed_at DESC)
|
|
45
|
-
"""
|
|
50
|
+
"""
|
|
51
|
+
)
|
|
46
52
|
|
|
47
53
|
conn.commit()
|
|
48
54
|
if not silent:
|
|
@@ -5,7 +5,8 @@ Migration 005: Add screenshots table for visual evidence management.
|
|
|
5
5
|
|
|
6
6
|
def upgrade(db):
|
|
7
7
|
"""Add screenshots table."""
|
|
8
|
-
db.execute(
|
|
8
|
+
db.execute(
|
|
9
|
+
"""
|
|
9
10
|
CREATE TABLE IF NOT EXISTS screenshots (
|
|
10
11
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
11
12
|
engagement_id INTEGER NOT NULL,
|
|
@@ -22,11 +23,18 @@ def upgrade(db):
|
|
|
22
23
|
FOREIGN KEY (host_id) REFERENCES hosts(id),
|
|
23
24
|
FOREIGN KEY (finding_id) REFERENCES findings(id)
|
|
24
25
|
)
|
|
25
|
-
"""
|
|
26
|
+
"""
|
|
27
|
+
)
|
|
26
28
|
|
|
27
|
-
db.execute(
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
db.execute(
|
|
30
|
+
"CREATE INDEX IF NOT EXISTS idx_screenshots_engagement ON screenshots(engagement_id)"
|
|
31
|
+
)
|
|
32
|
+
db.execute(
|
|
33
|
+
"CREATE INDEX IF NOT EXISTS idx_screenshots_host ON screenshots(host_id)"
|
|
34
|
+
)
|
|
35
|
+
db.execute(
|
|
36
|
+
"CREATE INDEX IF NOT EXISTS idx_screenshots_finding ON screenshots(finding_id)"
|
|
37
|
+
)
|
|
30
38
|
|
|
31
39
|
|
|
32
40
|
def downgrade(db):
|
|
@@ -5,7 +5,8 @@ Migration 006: Add deliverables tracking table.
|
|
|
5
5
|
|
|
6
6
|
def upgrade(db):
|
|
7
7
|
"""Add deliverables tracking table."""
|
|
8
|
-
db.execute(
|
|
8
|
+
db.execute(
|
|
9
|
+
"""
|
|
9
10
|
CREATE TABLE IF NOT EXISTS deliverables (
|
|
10
11
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
11
12
|
engagement_id INTEGER NOT NULL,
|
|
@@ -23,11 +24,18 @@ def upgrade(db):
|
|
|
23
24
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
24
25
|
FOREIGN KEY (engagement_id) REFERENCES engagements(id)
|
|
25
26
|
)
|
|
26
|
-
"""
|
|
27
|
+
"""
|
|
28
|
+
)
|
|
27
29
|
|
|
28
|
-
db.execute(
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
db.execute(
|
|
31
|
+
"CREATE INDEX IF NOT EXISTS idx_deliverables_engagement ON deliverables(engagement_id)"
|
|
32
|
+
)
|
|
33
|
+
db.execute(
|
|
34
|
+
"CREATE INDEX IF NOT EXISTS idx_deliverables_status ON deliverables(status)"
|
|
35
|
+
)
|
|
36
|
+
db.execute(
|
|
37
|
+
"CREATE INDEX IF NOT EXISTS idx_deliverables_category ON deliverables(category)"
|
|
38
|
+
)
|
|
31
39
|
|
|
32
40
|
|
|
33
41
|
def downgrade(db):
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Migration 007: Add deliverable templates system
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def upgrade(conn):
|
|
6
7
|
"""Add deliverable templates table."""
|
|
7
|
-
|
|
8
|
+
|
|
8
9
|
# Templates table
|
|
9
|
-
conn.execute(
|
|
10
|
+
conn.execute(
|
|
11
|
+
"""
|
|
10
12
|
CREATE TABLE IF NOT EXISTS deliverable_templates (
|
|
11
13
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
12
14
|
name TEXT NOT NULL,
|
|
@@ -18,14 +20,17 @@ def upgrade(conn):
|
|
|
18
20
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
19
21
|
is_builtin INTEGER DEFAULT 0
|
|
20
22
|
)
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
+
"""
|
|
24
|
+
)
|
|
25
|
+
|
|
23
26
|
# Index for faster lookups
|
|
24
|
-
conn.execute(
|
|
27
|
+
conn.execute(
|
|
28
|
+
"""
|
|
25
29
|
CREATE INDEX IF NOT EXISTS idx_templates_framework
|
|
26
30
|
ON deliverable_templates(framework, engagement_type)
|
|
27
|
-
"""
|
|
28
|
-
|
|
31
|
+
"""
|
|
32
|
+
)
|
|
33
|
+
|
|
29
34
|
print("✅ Migration 007: Deliverable templates table created")
|
|
30
35
|
|
|
31
36
|
|
|
@@ -5,7 +5,8 @@ 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(
|
|
8
|
+
db.execute(
|
|
9
|
+
"""
|
|
9
10
|
CREATE TABLE IF NOT EXISTS nuclei_findings (
|
|
10
11
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
11
12
|
engagement_id INTEGER NOT NULL,
|
|
@@ -24,10 +25,15 @@ def upgrade(db):
|
|
|
24
25
|
found_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
25
26
|
FOREIGN KEY(engagement_id) REFERENCES engagements(id) ON DELETE CASCADE
|
|
26
27
|
)
|
|
27
|
-
"""
|
|
28
|
+
"""
|
|
29
|
+
)
|
|
28
30
|
|
|
29
|
-
db.execute(
|
|
30
|
-
|
|
31
|
+
db.execute(
|
|
32
|
+
"CREATE INDEX IF NOT EXISTS idx_nuclei_engagement ON nuclei_findings(engagement_id)"
|
|
33
|
+
)
|
|
34
|
+
db.execute(
|
|
35
|
+
"CREATE INDEX IF NOT EXISTS idx_nuclei_severity ON nuclei_findings(severity)"
|
|
36
|
+
)
|
|
31
37
|
db.execute("CREATE INDEX IF NOT EXISTS idx_nuclei_cve ON nuclei_findings(cve_id)")
|
|
32
38
|
|
|
33
39
|
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Migration 010: Evidence linking system for deliverables
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def upgrade(conn):
|
|
6
7
|
"""Add evidence linking table."""
|
|
7
|
-
|
|
8
|
-
conn.execute(
|
|
8
|
+
|
|
9
|
+
conn.execute(
|
|
10
|
+
"""
|
|
9
11
|
CREATE TABLE IF NOT EXISTS deliverable_evidence (
|
|
10
12
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
11
13
|
deliverable_id INTEGER NOT NULL,
|
|
@@ -16,18 +18,23 @@ def upgrade(conn):
|
|
|
16
18
|
notes TEXT,
|
|
17
19
|
FOREIGN KEY (deliverable_id) REFERENCES deliverables(id) ON DELETE CASCADE
|
|
18
20
|
)
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
"""
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
conn.execute(
|
|
25
|
+
"""
|
|
22
26
|
CREATE INDEX IF NOT EXISTS idx_deliverable_evidence
|
|
23
27
|
ON deliverable_evidence(deliverable_id, evidence_type)
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
"""
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
conn.execute(
|
|
32
|
+
"""
|
|
27
33
|
CREATE INDEX IF NOT EXISTS idx_evidence_lookup
|
|
28
34
|
ON deliverable_evidence(evidence_type, evidence_id)
|
|
29
|
-
"""
|
|
30
|
-
|
|
35
|
+
"""
|
|
36
|
+
)
|
|
37
|
+
|
|
31
38
|
print("✅ Migration 010: Evidence linking system created")
|
|
32
39
|
|
|
33
40
|
|
|
@@ -2,56 +2,63 @@
|
|
|
2
2
|
Migration 011: Timeline tracking for deliverables and engagements
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def upgrade(conn):
|
|
6
7
|
"""Add timeline tracking columns."""
|
|
7
|
-
|
|
8
|
+
|
|
8
9
|
# Add timeline columns to deliverables
|
|
9
10
|
try:
|
|
10
11
|
conn.execute("ALTER TABLE deliverables ADD COLUMN started_at TIMESTAMP")
|
|
11
12
|
except Exception:
|
|
12
13
|
pass # Column might already exist
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
try:
|
|
15
16
|
conn.execute("ALTER TABLE deliverables ADD COLUMN completed_at TIMESTAMP")
|
|
16
17
|
except Exception:
|
|
17
18
|
pass
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
try:
|
|
20
|
-
conn.execute(
|
|
21
|
+
conn.execute(
|
|
22
|
+
"ALTER TABLE deliverables ADD COLUMN estimated_hours FLOAT DEFAULT 0"
|
|
23
|
+
)
|
|
21
24
|
except Exception:
|
|
22
25
|
pass
|
|
23
|
-
|
|
26
|
+
|
|
24
27
|
try:
|
|
25
28
|
conn.execute("ALTER TABLE deliverables ADD COLUMN actual_hours FLOAT DEFAULT 0")
|
|
26
29
|
except Exception:
|
|
27
30
|
pass
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
try:
|
|
30
33
|
conn.execute("ALTER TABLE deliverables ADD COLUMN blocker TEXT")
|
|
31
34
|
except Exception:
|
|
32
35
|
pass
|
|
33
|
-
|
|
36
|
+
|
|
34
37
|
try:
|
|
35
38
|
conn.execute("ALTER TABLE deliverables ADD COLUMN assigned_to TEXT")
|
|
36
39
|
except Exception:
|
|
37
40
|
pass
|
|
38
|
-
|
|
41
|
+
|
|
39
42
|
# Add engagement type to engagements
|
|
40
43
|
try:
|
|
41
|
-
conn.execute(
|
|
44
|
+
conn.execute(
|
|
45
|
+
"ALTER TABLE engagements ADD COLUMN engagement_type TEXT DEFAULT 'network'"
|
|
46
|
+
)
|
|
42
47
|
except Exception:
|
|
43
48
|
pass
|
|
44
|
-
|
|
49
|
+
|
|
45
50
|
try:
|
|
46
|
-
conn.execute(
|
|
51
|
+
conn.execute(
|
|
52
|
+
"ALTER TABLE engagements ADD COLUMN estimated_hours FLOAT DEFAULT 0"
|
|
53
|
+
)
|
|
47
54
|
except Exception:
|
|
48
55
|
pass
|
|
49
|
-
|
|
56
|
+
|
|
50
57
|
try:
|
|
51
58
|
conn.execute("ALTER TABLE engagements ADD COLUMN actual_hours FLOAT DEFAULT 0")
|
|
52
59
|
except Exception:
|
|
53
60
|
pass
|
|
54
|
-
|
|
61
|
+
|
|
55
62
|
print("✅ Migration 011: Timeline tracking columns added")
|
|
56
63
|
|
|
57
64
|
|
|
@@ -2,22 +2,24 @@
|
|
|
2
2
|
Migration 012: Add team collaboration features
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def upgrade(conn):
|
|
6
7
|
"""Add team collaboration tables."""
|
|
7
|
-
|
|
8
|
+
|
|
8
9
|
# Add columns to deliverables table for team collaboration
|
|
9
10
|
try:
|
|
10
11
|
conn.execute("ALTER TABLE deliverables ADD COLUMN assigned_to TEXT")
|
|
11
12
|
except Exception:
|
|
12
13
|
pass # Column may already exist
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
try:
|
|
15
16
|
conn.execute("ALTER TABLE deliverables ADD COLUMN blocker TEXT")
|
|
16
17
|
except Exception:
|
|
17
18
|
pass # Column may already exist
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
# Activity log for tracking who did what
|
|
20
|
-
conn.execute(
|
|
21
|
+
conn.execute(
|
|
22
|
+
"""
|
|
21
23
|
CREATE TABLE IF NOT EXISTS deliverable_activity (
|
|
22
24
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
23
25
|
deliverable_id INTEGER NOT NULL,
|
|
@@ -29,26 +31,34 @@ def upgrade(conn):
|
|
|
29
31
|
FOREIGN KEY (deliverable_id) REFERENCES deliverables(id) ON DELETE CASCADE,
|
|
30
32
|
FOREIGN KEY (engagement_id) REFERENCES engagements(id) ON DELETE CASCADE
|
|
31
33
|
)
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
+
"""
|
|
35
|
+
)
|
|
36
|
+
|
|
34
37
|
# Index for faster activity queries
|
|
35
|
-
conn.execute(
|
|
38
|
+
conn.execute(
|
|
39
|
+
"""
|
|
36
40
|
CREATE INDEX IF NOT EXISTS idx_activity_deliverable
|
|
37
41
|
ON deliverable_activity(deliverable_id, created_at DESC)
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
"""
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
conn.execute(
|
|
46
|
+
"""
|
|
41
47
|
CREATE INDEX IF NOT EXISTS idx_activity_engagement
|
|
42
48
|
ON deliverable_activity(engagement_id, created_at DESC)
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
"""
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
conn.execute(
|
|
53
|
+
"""
|
|
46
54
|
CREATE INDEX IF NOT EXISTS idx_activity_user
|
|
47
55
|
ON deliverable_activity(user, created_at DESC)
|
|
48
|
-
"""
|
|
49
|
-
|
|
56
|
+
"""
|
|
57
|
+
)
|
|
58
|
+
|
|
50
59
|
# Comments on deliverables
|
|
51
|
-
conn.execute(
|
|
60
|
+
conn.execute(
|
|
61
|
+
"""
|
|
52
62
|
CREATE TABLE IF NOT EXISTS deliverable_comments (
|
|
53
63
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
54
64
|
deliverable_id INTEGER NOT NULL,
|
|
@@ -57,13 +67,16 @@ def upgrade(conn):
|
|
|
57
67
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
58
68
|
FOREIGN KEY (deliverable_id) REFERENCES deliverables(id) ON DELETE CASCADE
|
|
59
69
|
)
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
"""
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
conn.execute(
|
|
74
|
+
"""
|
|
63
75
|
CREATE INDEX IF NOT EXISTS idx_comments_deliverable
|
|
64
76
|
ON deliverable_comments(deliverable_id, created_at DESC)
|
|
65
|
-
"""
|
|
66
|
-
|
|
77
|
+
"""
|
|
78
|
+
)
|
|
79
|
+
|
|
67
80
|
print("✅ Migration 012: Team collaboration tables created")
|
|
68
81
|
|
|
69
82
|
|