souleyez 2.28.0__py3-none-any.whl → 2.39.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of souleyez might be problematic. Click here for more details.

souleyez/__init__.py CHANGED
@@ -1 +1,2 @@
1
- __version__ = '2.28.0'
1
+ __version__ = '2.39.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:
@@ -1759,6 +1759,20 @@ class ToolChaining:
1759
1759
  )
1760
1760
  )
1761
1761
 
1762
+ # Database Admin → SQLMap (gentler settings for phpMyAdmin/Adminer)
1763
+ # These panels are slow and easily overwhelmed - use single thread and basic tests
1764
+ self.rules.append(
1765
+ ChainRule(
1766
+ trigger_tool='gobuster',
1767
+ trigger_condition='category:database_admin',
1768
+ target_tool='sqlmap',
1769
+ priority=6, # Lower priority than CVE/exploit scans
1770
+ args_template=['-u', '{target}', '--batch', '--forms', '--threads=1', '--time-sec=10',
1771
+ '--level=1', '--risk=1', '--technique=BEU', '--timeout=30'],
1772
+ description='Database admin panel detected, testing login form for SQL injection (low intensity)'
1773
+ )
1774
+ )
1775
+
1762
1776
  # WordPress → WPScan enumeration
1763
1777
  self.rules.append(
1764
1778
  ChainRule(
@@ -3958,7 +3972,7 @@ class ToolChaining:
3958
3972
  # This reduces noise and focuses on high-value targets
3959
3973
  from souleyez.intelligence.sensitive_tables import is_sensitive_table, is_system_table
3960
3974
 
3961
- MAX_TABLES_FOR_COLUMN_ENUM = 15 # Focused on sensitive tables only
3975
+ MAX_TABLES_FOR_COLUMN_ENUM = 10 # Focused on sensitive tables only
3962
3976
  tables_queued = 0
3963
3977
  skipped_tables = 0
3964
3978
 
@@ -5017,6 +5031,7 @@ class ToolChaining:
5017
5031
  label=f"Auto-retry: gobuster (wildcard {exclude_length}b)",
5018
5032
  engagement_id=engagement_id,
5019
5033
  parent_id=job.get('id'),
5034
+ reason=f"Auto-triggered by gobuster: Wildcard response detected, retrying with --exclude-length {exclude_length}",
5020
5035
  metadata={'retry_attempt': 1, 'retry_parent_job_id': job.get('id')}
5021
5036
  )
5022
5037
 
@@ -5116,7 +5131,8 @@ class ToolChaining:
5116
5131
  args=sqlmap_args,
5117
5132
  label=f"Auto-chain: SQLMap testing {endpoint_url}",
5118
5133
  engagement_id=engagement_id,
5119
- parent_id=job.get('id')
5134
+ parent_id=job.get('id'),
5135
+ reason=f"Auto-triggered by ffuf: Database/dynamic endpoint detected ({status_code} response)"
5120
5136
  )
5121
5137
 
5122
5138
  job_ids.append(sqlmap_job_id)
@@ -5144,6 +5160,7 @@ class ToolChaining:
5144
5160
  label=f"Auto-chain: ffuf recursive {endpoint_url}",
5145
5161
  engagement_id=engagement_id,
5146
5162
  parent_id=job.get('id'),
5163
+ reason=f"Auto-triggered by ffuf: {status_code} response suggests deeper path, fuzzing recursively",
5147
5164
  metadata={'ffuf_depth': current_depth + 1}
5148
5165
  )
5149
5166
 
@@ -5367,7 +5384,8 @@ class ToolChaining:
5367
5384
  args=['-m', '18200', '-a', '0', 'data/wordlists/top100.txt'],
5368
5385
  label='CRACK_ASREP',
5369
5386
  engagement_id=engagement_id,
5370
- parent_id=job.get('id')
5387
+ parent_id=job.get('id'),
5388
+ reason="Auto-triggered by impacket-getnpusers: AS-REP hash extracted, attempting to crack"
5371
5389
  )
5372
5390
 
5373
5391
  job_ids.append(job_id)
@@ -5412,7 +5430,8 @@ class ToolChaining:
5412
5430
  args=['-m', '1000', '-a', '0', 'data/wordlists/top100.txt'],
5413
5431
  label='CRACK_NTLM',
5414
5432
  engagement_id=engagement_id,
5415
- parent_id=job.get('id')
5433
+ parent_id=job.get('id'),
5434
+ reason="Auto-triggered by impacket-secretsdump: NTLM hash extracted, attempting to crack"
5416
5435
  )
5417
5436
 
5418
5437
  job_ids.append(job_id)
@@ -5452,7 +5471,8 @@ class ToolChaining:
5452
5471
  args=[cred_str],
5453
5472
  label='EXTRACT_CREDS',
5454
5473
  engagement_id=engagement_id,
5455
- parent_id=job.get('id')
5474
+ parent_id=job.get('id'),
5475
+ reason="Auto-triggered by hydra: Valid credentials found, attempting to extract domain secrets"
5456
5476
  )
5457
5477
 
5458
5478
  job_ids.append(job_id)
@@ -5750,6 +5770,17 @@ class ToolChaining:
5750
5770
 
5751
5771
  status = existing_job.get('status')
5752
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
+
5753
5784
  # Check if job is active (queued/running)
5754
5785
  is_active = status in ['queued', 'running']
5755
5786
 
@@ -5764,11 +5795,28 @@ class ToolChaining:
5764
5795
  current_time = datetime.now(finished_time.tzinfo) if finished_time.tzinfo else datetime.now()
5765
5796
  time_delta = (current_time - finished_time).total_seconds()
5766
5797
 
5767
- # 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)
5768
5799
  if time_delta < DUPLICATE_WINDOW_SECONDS:
5769
5800
  existing_args = existing_job.get('args', [])
5770
5801
  cmd_args = cmd.get('args', [])
5771
- 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:
5772
5820
  is_recent_duplicate = True
5773
5821
  minutes_ago = int(time_delta // 60)
5774
5822
  seconds_ago = int(time_delta % 60)
@@ -5790,16 +5838,34 @@ class ToolChaining:
5790
5838
  print(f" ⏭️ Skipping {cmd['tool']} for {cmd_target}: similar job #{existing_job['id']} already exists ({existing_job['status']})")
5791
5839
  break
5792
5840
 
5793
- # For sqlmap, gobuster: check if args match (allow different ports/phases)
5794
- 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':
5795
5863
  existing_args = existing_job.get('args', [])
5796
5864
  cmd_args = cmd.get('args', [])
5797
- # Compare args - if they're the same, it's a duplicate
5798
5865
  if existing_args == cmd_args:
5799
5866
  similar_exists = True
5800
5867
  print(f" ⏭️ Skipping {cmd['tool']} for {cmd_target}: similar job #{existing_job['id']} already exists ({existing_job['status']})")
5801
5868
  break
5802
- # If args are different, allow it (different SQLMap phase)
5803
5869
 
5804
5870
  # For quick lookup tools (whois, dnsrecon), skip 5-min duplicate check
5805
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.28.0
3
+ **Version:** 2.39.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(), {