souleyez 2.32.0__py3-none-any.whl → 2.40.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 CHANGED
@@ -1 +1,2 @@
1
- __version__ = '2.32.0'
1
+ __version__ = '2.40.0'
2
+
@@ -49,8 +49,9 @@ class MSFAutoMapper:
49
49
  risk_levels=['safe', 'noisy', 'moderate', 'dangerous']
50
50
  )
51
51
 
52
- # Filter to top 10 recommendations
53
- service_map[service_id] = recommendations[:10]
52
+ # Only store services with actual recommendations
53
+ if recommendations:
54
+ service_map[service_id] = recommendations[:10]
54
55
 
55
56
  return service_map
56
57
  except Exception as e:
@@ -3972,7 +3972,7 @@ class ToolChaining:
3972
3972
  # This reduces noise and focuses on high-value targets
3973
3973
  from souleyez.intelligence.sensitive_tables import is_sensitive_table, is_system_table
3974
3974
 
3975
- MAX_TABLES_FOR_COLUMN_ENUM = 15 # Focused on sensitive tables only
3975
+ MAX_TABLES_FOR_COLUMN_ENUM = 10 # Focused on sensitive tables only
3976
3976
  tables_queued = 0
3977
3977
  skipped_tables = 0
3978
3978
 
@@ -5770,6 +5770,17 @@ class ToolChaining:
5770
5770
 
5771
5771
  status = existing_job.get('status')
5772
5772
 
5773
+ # === SQLMAP RULE-BASED DEDUP (check ALL completed jobs, not just recent) ===
5774
+ # For sqlmap: if same rule was already applied to this URL, skip it entirely
5775
+ # This prevents infinite loops where each sqlmap job re-triggers the same rules
5776
+ if cmd['tool'] == 'sqlmap' and status in ['done', 'queued', 'running']:
5777
+ cmd_rule_id = cmd.get('rule_id')
5778
+ existing_rule_id = existing_job.get('rule_id')
5779
+ if cmd_rule_id and existing_rule_id and cmd_rule_id == existing_rule_id:
5780
+ similar_exists = True
5781
+ print(f" ⏭️ Skipping sqlmap for {cmd_target}: rule #{cmd_rule_id} already applied (job #{existing_job['id']} {status})")
5782
+ break
5783
+
5773
5784
  # Check if job is active (queued/running)
5774
5785
  is_active = status in ['queued', 'running']
5775
5786
 
@@ -5784,11 +5795,28 @@ class ToolChaining:
5784
5795
  current_time = datetime.now(finished_time.tzinfo) if finished_time.tzinfo else datetime.now()
5785
5796
  time_delta = (current_time - finished_time).total_seconds()
5786
5797
 
5787
- # Only block if same args AND finished < 5 min ago
5798
+ # Only block if finished < 5 min ago AND (same args OR same rule_id for sqlmap)
5788
5799
  if time_delta < DUPLICATE_WINDOW_SECONDS:
5789
5800
  existing_args = existing_job.get('args', [])
5790
5801
  cmd_args = cmd.get('args', [])
5791
- if existing_args == cmd_args:
5802
+
5803
+ # For sqlmap: also check rule_id (same rule = duplicate even with different parent)
5804
+ if cmd['tool'] == 'sqlmap':
5805
+ cmd_rule_id = cmd.get('rule_id')
5806
+ existing_rule_id = existing_job.get('rule_id')
5807
+ if cmd_rule_id and existing_rule_id and cmd_rule_id == existing_rule_id:
5808
+ is_recent_duplicate = True
5809
+ minutes_ago = int(time_delta // 60)
5810
+ seconds_ago = int(time_delta % 60)
5811
+ time_str = f"{minutes_ago}m {seconds_ago}s ago" if minutes_ago > 0 else f"{seconds_ago}s ago"
5812
+ print(f" ⏭️ Skipping sqlmap for {cmd_target}: rule #{cmd_rule_id} completed {time_str} (duplicate)")
5813
+ elif existing_args == cmd_args:
5814
+ is_recent_duplicate = True
5815
+ minutes_ago = int(time_delta // 60)
5816
+ seconds_ago = int(time_delta % 60)
5817
+ time_str = f"{minutes_ago}m {seconds_ago}s ago" if minutes_ago > 0 else f"{seconds_ago}s ago"
5818
+ print(f" ⏭️ Skipping sqlmap for {cmd_target}: job #{existing_job['id']} completed {time_str} (duplicate)")
5819
+ elif existing_args == cmd_args:
5792
5820
  is_recent_duplicate = True
5793
5821
  minutes_ago = int(time_delta // 60)
5794
5822
  seconds_ago = int(time_delta % 60)
@@ -5810,16 +5838,34 @@ class ToolChaining:
5810
5838
  print(f" ⏭️ Skipping {cmd['tool']} for {cmd_target}: similar job #{existing_job['id']} already exists ({existing_job['status']})")
5811
5839
  break
5812
5840
 
5813
- # For sqlmap, gobuster: check if args match (allow different ports/phases)
5814
- elif cmd['tool'] in ['sqlmap', 'gobuster']:
5841
+ # For sqlmap: check by rule_id to prevent same rule firing twice on same URL
5842
+ elif cmd['tool'] == 'sqlmap':
5843
+ cmd_rule_id = cmd.get('rule_id')
5844
+ existing_rule_id = existing_job.get('rule_id')
5845
+ existing_args = existing_job.get('args', [])
5846
+ cmd_args = cmd.get('args', [])
5847
+
5848
+ # If both have rule_id and they match, it's a duplicate
5849
+ # (same rule already applied to this injection point)
5850
+ if cmd_rule_id and existing_rule_id and cmd_rule_id == existing_rule_id:
5851
+ similar_exists = True
5852
+ print(f" ⏭️ Skipping sqlmap for {cmd_target}: rule #{cmd_rule_id} already applied (job #{existing_job['id']} {existing_job['status']})")
5853
+ break
5854
+ # Also check if exact same args (covers manual/no-rule jobs)
5855
+ elif existing_args == cmd_args:
5856
+ similar_exists = True
5857
+ print(f" ⏭️ Skipping sqlmap for {cmd_target}: similar job #{existing_job['id']} already exists ({existing_job['status']})")
5858
+ break
5859
+ # If different rule_id and different args, allow it (different SQLMap phase)
5860
+
5861
+ # For gobuster: check if args match
5862
+ elif cmd['tool'] == 'gobuster':
5815
5863
  existing_args = existing_job.get('args', [])
5816
5864
  cmd_args = cmd.get('args', [])
5817
- # Compare args - if they're the same, it's a duplicate
5818
5865
  if existing_args == cmd_args:
5819
5866
  similar_exists = True
5820
5867
  print(f" ⏭️ Skipping {cmd['tool']} for {cmd_target}: similar job #{existing_job['id']} already exists ({existing_job['status']})")
5821
5868
  break
5822
- # If args are different, allow it (different SQLMap phase)
5823
5869
 
5824
5870
  # For quick lookup tools (whois, dnsrecon), skip 5-min duplicate check
5825
5871
  # They're fast and each theHarvester run should trigger them
souleyez/docs/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SoulEyez Documentation
2
2
 
3
- **Version:** 2.32.0
3
+ **Version:** 2.40.0
4
4
  **Last Updated:** January 9, 2026
5
5
  **Organization:** CyberSoul Security
6
6
 
@@ -30,6 +30,7 @@ from souleyez.integrations.siem.wazuh import WazuhSIEMClient
30
30
  from souleyez.integrations.siem.splunk import SplunkSIEMClient
31
31
  from souleyez.integrations.siem.elastic import ElasticSIEMClient
32
32
  from souleyez.integrations.siem.sentinel import SentinelSIEMClient
33
+ from souleyez.integrations.siem.googlesecops import GoogleSecOpsSIEMClient
33
34
  from souleyez.integrations.siem.factory import SIEMFactory
34
35
 
35
36
  __all__ = [
@@ -45,4 +46,5 @@ __all__ = [
45
46
  'SplunkSIEMClient',
46
47
  'ElasticSIEMClient',
47
48
  'SentinelSIEMClient',
49
+ 'GoogleSecOpsSIEMClient',
48
50
  ]
@@ -11,7 +11,8 @@ from souleyez.integrations.siem.base import SIEMClient, SIEMConnectionStatus
11
11
 
12
12
 
13
13
  # Registry of available SIEM types
14
- SIEM_TYPES = ['wazuh', 'splunk', 'elastic', 'sentinel']
14
+ # Ordered: Open Source first, then Commercial
15
+ SIEM_TYPES = ['wazuh', 'elastic', 'splunk', 'sentinel', 'google_secops']
15
16
 
16
17
 
17
18
  class SIEMFactory:
@@ -60,6 +61,10 @@ class SIEMFactory:
60
61
  from souleyez.integrations.siem.sentinel import SentinelSIEMClient
61
62
  return SentinelSIEMClient.from_config(config)
62
63
 
64
+ elif siem_type_lower == 'google_secops':
65
+ from souleyez.integrations.siem.googlesecops import GoogleSecOpsSIEMClient
66
+ return GoogleSecOpsSIEMClient.from_config(config)
67
+
63
68
  else:
64
69
  raise ValueError(
65
70
  f"Unsupported SIEM type: {siem_type}. "
@@ -114,7 +119,7 @@ class SIEMFactory:
114
119
  info_map = {
115
120
  'wazuh': {
116
121
  'name': 'Wazuh',
117
- 'description': 'Open source security monitoring (OSSEC fork)',
122
+ 'description': '[Open Source] Security monitoring platform (OSSEC fork)',
118
123
  'config_fields': [
119
124
  {'name': 'api_url', 'label': 'Manager API URL', 'required': True,
120
125
  'placeholder': 'https://wazuh.example.com:55000'},
@@ -130,7 +135,7 @@ class SIEMFactory:
130
135
  },
131
136
  'splunk': {
132
137
  'name': 'Splunk',
133
- 'description': 'Enterprise SIEM and log management platform',
138
+ 'description': '[Commercial] Enterprise SIEM and log management',
134
139
  'config_fields': [
135
140
  {'name': 'api_url', 'label': 'REST API URL', 'required': True,
136
141
  'placeholder': 'https://splunk.example.com:8089'},
@@ -144,7 +149,7 @@ class SIEMFactory:
144
149
  },
145
150
  'elastic': {
146
151
  'name': 'Elastic Security',
147
- 'description': 'Elastic SIEM (formerly Elastic Security)',
152
+ 'description': '[Open Source] Elastic Stack security solution (ELK SIEM)',
148
153
  'config_fields': [
149
154
  {'name': 'elasticsearch_url', 'label': 'Elasticsearch URL', 'required': True,
150
155
  'placeholder': 'https://elastic.example.com:9200'},
@@ -159,7 +164,7 @@ class SIEMFactory:
159
164
  },
160
165
  'sentinel': {
161
166
  'name': 'Microsoft Sentinel',
162
- 'description': 'Azure cloud-native SIEM',
167
+ 'description': '[Commercial] Azure cloud-native SIEM',
163
168
  'config_fields': [
164
169
  {'name': 'tenant_id', 'label': 'Azure Tenant ID', 'required': True},
165
170
  {'name': 'client_id', 'label': 'App Client ID', 'required': True},
@@ -170,6 +175,22 @@ class SIEMFactory:
170
175
  {'name': 'workspace_id', 'label': 'Workspace ID (GUID)', 'required': True},
171
176
  ],
172
177
  },
178
+ 'google_secops': {
179
+ 'name': 'Google SecOps',
180
+ 'description': '[Commercial] Google Cloud security operations (Chronicle)',
181
+ 'config_fields': [
182
+ {'name': 'customer_id', 'label': 'Chronicle Customer ID', 'required': True,
183
+ 'placeholder': 'Your Chronicle customer ID'},
184
+ {'name': 'region', 'label': 'Chronicle Region', 'required': True,
185
+ 'placeholder': 'us, europe, asia-southeast1'},
186
+ {'name': 'project_id', 'label': 'Google Cloud Project ID', 'required': False,
187
+ 'placeholder': 'Optional if in service account JSON'},
188
+ {'name': 'credentials_json', 'label': 'Service Account JSON', 'required': True,
189
+ 'secret': True, 'type': 'textarea',
190
+ 'placeholder': 'Paste service account JSON key'},
191
+ {'name': 'verify_ssl', 'label': 'Verify SSL', 'required': False, 'type': 'boolean'},
192
+ ],
193
+ },
173
194
  }
174
195
 
175
196
  return info_map.get(siem_type.lower(), {