regscale-cli 6.25.1.0__py3-none-any.whl → 6.27.0.0__py3-none-any.whl

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

Potentially problematic release.


This version of regscale-cli might be problematic. Click here for more details.

Files changed (146) hide show
  1. regscale/_version.py +1 -1
  2. regscale/airflow/hierarchy.py +2 -2
  3. regscale/core/app/application.py +19 -4
  4. regscale/core/app/internal/evidence.py +419 -2
  5. regscale/core/app/internal/login.py +0 -1
  6. regscale/core/app/utils/catalog_utils/common.py +1 -1
  7. regscale/dev/code_gen.py +24 -20
  8. regscale/integrations/commercial/jira.py +367 -126
  9. regscale/integrations/commercial/qualys/__init__.py +7 -8
  10. regscale/integrations/commercial/qualys/scanner.py +8 -3
  11. regscale/integrations/commercial/sicura/api.py +14 -13
  12. regscale/integrations/commercial/sicura/commands.py +8 -2
  13. regscale/integrations/commercial/sicura/scanner.py +49 -39
  14. regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
  15. regscale/integrations/commercial/synqly/assets.py +17 -0
  16. regscale/integrations/commercial/synqly/vulnerabilities.py +45 -28
  17. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  18. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  19. regscale/integrations/commercial/tenablev2/commands.py +142 -1
  20. regscale/integrations/commercial/tenablev2/scanner.py +0 -1
  21. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  22. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  23. regscale/integrations/commercial/wizv2/click.py +64 -79
  24. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  25. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  26. regscale/integrations/commercial/wizv2/compliance_report.py +161 -165
  27. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  28. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +3 -3
  29. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +1 -17
  30. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  31. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  32. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +5 -9
  33. regscale/integrations/commercial/wizv2/issue.py +1 -1
  34. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  35. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  36. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  37. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  38. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  39. regscale/integrations/commercial/wizv2/reports.py +1 -1
  40. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  41. regscale/integrations/commercial/wizv2/scanner.py +39 -99
  42. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  43. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  44. regscale/integrations/commercial/wizv2/variables.py +89 -3
  45. regscale/integrations/compliance_integration.py +60 -41
  46. regscale/integrations/control_matcher.py +377 -0
  47. regscale/integrations/due_date_handler.py +14 -8
  48. regscale/integrations/milestone_manager.py +291 -0
  49. regscale/integrations/public/__init__.py +1 -0
  50. regscale/integrations/public/cci_importer.py +37 -38
  51. regscale/integrations/public/fedramp/click.py +60 -2
  52. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  53. regscale/integrations/public/fedramp/fedramp_cis_crm.py +393 -340
  54. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  55. regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
  56. regscale/integrations/scanner_integration.py +277 -153
  57. regscale/models/integration_models/cisa_kev_data.json +282 -9
  58. regscale/models/integration_models/nexpose.py +36 -10
  59. regscale/models/integration_models/qualys.py +3 -4
  60. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  61. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +24 -7
  62. regscale/models/integration_models/synqly_models/synqly_model.py +8 -1
  63. regscale/models/locking.py +12 -8
  64. regscale/models/platform.py +1 -2
  65. regscale/models/regscale_models/control_implementation.py +47 -22
  66. regscale/models/regscale_models/issue.py +256 -95
  67. regscale/models/regscale_models/milestone.py +1 -1
  68. regscale/models/regscale_models/regscale_model.py +6 -1
  69. regscale/templates/__init__.py +0 -0
  70. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/METADATA +1 -17
  71. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/RECORD +145 -65
  72. tests/regscale/integrations/commercial/__init__.py +0 -0
  73. tests/regscale/integrations/commercial/conftest.py +28 -0
  74. tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
  75. tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
  76. tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
  77. tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
  78. tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
  79. tests/regscale/integrations/commercial/test_aws.py +3731 -0
  80. tests/regscale/integrations/commercial/test_burp.py +48 -0
  81. tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
  82. tests/regscale/integrations/commercial/test_dependabot.py +341 -0
  83. tests/regscale/integrations/commercial/test_gcp.py +1543 -0
  84. tests/regscale/integrations/commercial/test_gitlab.py +549 -0
  85. tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
  86. tests/regscale/integrations/commercial/test_jira.py +2204 -0
  87. tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
  88. tests/regscale/integrations/commercial/test_okta.py +1228 -0
  89. tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
  90. tests/regscale/integrations/commercial/test_sicura.py +350 -0
  91. tests/regscale/integrations/commercial/test_snow.py +423 -0
  92. tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
  93. tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
  94. tests/regscale/integrations/commercial/test_stig.py +33 -0
  95. tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
  96. tests/regscale/integrations/commercial/test_stigv2.py +406 -0
  97. tests/regscale/integrations/commercial/test_wiz.py +1365 -0
  98. tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
  99. tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
  100. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  101. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  102. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  103. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  104. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  105. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  106. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  107. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  108. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  109. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  110. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  111. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  112. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  113. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  114. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  115. tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
  116. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  117. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  118. tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
  119. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  120. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  121. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  122. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  123. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
  124. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
  125. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
  126. tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
  127. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  128. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  129. tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
  130. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1132 -0
  131. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
  132. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  133. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  134. tests/regscale/integrations/public/fedramp/__init__.py +1 -0
  135. tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
  136. tests/regscale/integrations/public/test_fedramp.py +301 -0
  137. tests/regscale/integrations/test_control_matcher.py +1397 -0
  138. tests/regscale/integrations/test_control_matching.py +155 -0
  139. tests/regscale/integrations/test_milestone_manager.py +408 -0
  140. tests/regscale/models/test_issue.py +378 -1
  141. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3543
  142. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  143. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/LICENSE +0 -0
  144. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/WHEEL +0 -0
  145. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/entry_points.txt +0 -0
  146. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,447 @@
1
+ """
2
+ Module for Tenable CIS Benchmark checklist integration.
3
+
4
+ This module provides integration classes for importing CIS (Center for Internet Security)
5
+ benchmark compliance data from Tenable Security Center and Tenable.io into RegScale
6
+ as checklist items.
7
+ """
8
+
9
+ import logging
10
+ from typing import Any, Generator, Iterator, List, Optional, Tuple
11
+
12
+ from regscale.core.app.utils.app_utils import get_current_datetime
13
+ from regscale.integrations.commercial.tenablev2.authenticate import gen_tio, gen_tsc
14
+ from regscale.integrations.commercial.tenablev2.cis_parsers import (
15
+ parse_cis_compliance_result,
16
+ parse_tenable_sc_cis_result,
17
+ )
18
+ from regscale.integrations.commercial.tenablev2.utils import get_filtered_severities
19
+ from regscale.integrations.scanner_integration import (
20
+ IntegrationAsset,
21
+ IntegrationFinding,
22
+ ScannerIntegration,
23
+ ScannerIntegrationType,
24
+ )
25
+ from regscale.models import regscale_models
26
+
27
+ logger = logging.getLogger("regscale")
28
+
29
+ # Constants
30
+ _PROGRESS_LOG_INTERVAL = 100 # Log progress every N findings
31
+
32
+
33
+ class TenableIOCISChecklistIntegration(ScannerIntegration):
34
+ """
35
+ Tenable.io CIS Benchmark Checklist Integration.
36
+
37
+ This integration class fetches CIS benchmark compliance data from Tenable.io
38
+ using the compliance export API and maps it to RegScale checklist items.
39
+
40
+ Attributes:
41
+ title: Integration title displayed in RegScale
42
+ type: Integration type (CHECKLIST for compliance data)
43
+ asset_identifier_field: Field name used for asset identification
44
+ """
45
+
46
+ title = "Tenable.io CIS Benchmarks"
47
+ type = ScannerIntegrationType.CHECKLIST
48
+ asset_identifier_field = "tenableId"
49
+
50
+ # Map CIS compliance status to RegScale checklist status
51
+ checklist_status_map = {
52
+ "PASSED": regscale_models.ChecklistStatus.PASS,
53
+ "FAILED": regscale_models.ChecklistStatus.FAIL,
54
+ "WARNING": regscale_models.ChecklistStatus.NOT_REVIEWED,
55
+ "ERROR": regscale_models.ChecklistStatus.FAIL,
56
+ "NOT_APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
57
+ }
58
+
59
+ # Map severity levels for CIS findings
60
+ finding_severity_map = {
61
+ "critical": regscale_models.IssueSeverity.Critical,
62
+ "high": regscale_models.IssueSeverity.High,
63
+ "medium": regscale_models.IssueSeverity.Moderate,
64
+ "low": regscale_models.IssueSeverity.Low,
65
+ }
66
+
67
+ def __init__(
68
+ self,
69
+ plan_id: int,
70
+ tenant_id: int = 1,
71
+ tags: Optional[List[Tuple[str, str]]] = None,
72
+ audit_file_filter: Optional[str] = None,
73
+ cis_level: Optional[str] = None,
74
+ **kwargs: Any,
75
+ ) -> None:
76
+ """
77
+ Initialize the Tenable.io CIS Benchmark integration.
78
+
79
+ :param int plan_id: The RegScale security plan ID
80
+ :param int tenant_id: The RegScale tenant ID, defaults to 1
81
+ :param Optional[List[Tuple[str, str]]] tags: Asset tags to filter by (e.g., [('environment', 'prod')])
82
+ :param Optional[str] audit_file_filter: Filter for specific audit files (e.g., "CIS_AlmaLinux*")
83
+ :param Optional[str] cis_level: Filter by CIS level ("1" or "2")
84
+ :param Any kwargs: Additional keyword arguments
85
+ """
86
+ super().__init__(plan_id, tenant_id, **kwargs)
87
+ self.client = None
88
+ self.tags = tags or []
89
+ self.audit_file_filter = audit_file_filter or "CIS_*"
90
+ self.cis_level = cis_level
91
+ self.scan_date = kwargs.get("scan_date", get_current_datetime())
92
+
93
+ def authenticate(self) -> None:
94
+ """Authenticate to Tenable.io."""
95
+ self.client = gen_tio()
96
+
97
+ def fetch_assets(self, **kwargs: Any) -> Iterator[IntegrationAsset]:
98
+ """
99
+ Fetch assets from Tenable.io.
100
+
101
+ For CIS benchmark integration, assets are typically pre-existing in RegScale
102
+ and linked via asset identifiers in the compliance findings.
103
+
104
+ :param Any kwargs: Additional keyword arguments
105
+ :yields: IntegrationAsset objects
106
+ :return: Iterator of IntegrationAsset objects
107
+ :rtype: Iterator[IntegrationAsset]
108
+ """
109
+ # CIS benchmark findings reference existing assets
110
+ # Assets should be synced separately using the standard Tenable asset integration
111
+ integration_assets = kwargs.get("integration_assets", [])
112
+ yield from integration_assets
113
+
114
+ def fetch_findings(self, **kwargs: Any) -> Generator[IntegrationFinding, None, None]:
115
+ """
116
+ Fetch CIS benchmark compliance findings from Tenable.io.
117
+
118
+ Uses the Tenable.io compliance export API to retrieve CIS benchmark
119
+ compliance check results and converts them to IntegrationFinding objects.
120
+
121
+ :param Any kwargs: Additional keyword arguments
122
+ :yields: IntegrationFinding objects
123
+ :return: Generator of IntegrationFinding objects
124
+ :rtype: Generator[IntegrationFinding, None, None]
125
+ """
126
+ logger.info("Fetching CIS benchmark compliance findings from Tenable.io...")
127
+
128
+ self.authenticate()
129
+
130
+ if not self.client:
131
+ raise ValueError("Tenable.io client not authenticated")
132
+
133
+ # Build filter criteria
134
+ filters = {
135
+ "audit_file_name": self.audit_file_filter,
136
+ "compliance_results": ["FAILED", "WARNING"], # Focus on non-passing checks
137
+ }
138
+
139
+ # Add tag filtering if specified
140
+ if self.tags:
141
+ filters["tags"] = self.tags
142
+
143
+ # Fetch compliance export
144
+ try:
145
+ compliance_iterator = self.client.exports.compliance(**filters)
146
+
147
+ findings_count = 0
148
+ for compliance_finding in compliance_iterator:
149
+ # Filter by CIS level if specified
150
+ if self.cis_level:
151
+ audit_file = compliance_finding.get("audit_file", "")
152
+ if f"_L{self.cis_level}." not in audit_file:
153
+ continue
154
+
155
+ # Convert compliance finding to IntegrationFinding
156
+ if finding := self._parse_compliance_finding(compliance_finding):
157
+ findings_count += 1
158
+ if findings_count % _PROGRESS_LOG_INTERVAL == 0:
159
+ logger.info(f"Processed {findings_count} CIS compliance findings")
160
+ yield finding
161
+
162
+ self.num_findings_to_process = findings_count
163
+ logger.info(f"Total CIS compliance findings processed: {findings_count}")
164
+
165
+ except Exception as e:
166
+ logger.error(f"Error fetching CIS compliance findings: {str(e)}", exc_info=True)
167
+ raise
168
+
169
+ def _parse_compliance_finding(self, compliance_data: dict) -> Optional[IntegrationFinding]:
170
+ """
171
+ Parse a Tenable.io compliance finding into an IntegrationFinding.
172
+
173
+ :param dict compliance_data: The compliance data from Tenable.io export
174
+ :return: IntegrationFinding object or None if parsing fails
175
+ :rtype: Optional[IntegrationFinding]
176
+ """
177
+ try:
178
+ # Extract asset identifier
179
+ asset_data = compliance_data.get("asset", {})
180
+ asset_uuid = asset_data.get("uuid", "")
181
+ asset_id = asset_data.get("id", "")
182
+ asset_identifier = asset_uuid or asset_id
183
+
184
+ if not asset_identifier:
185
+ logger.warning("Compliance finding missing asset identifier, skipping")
186
+ return None
187
+
188
+ # Create base finding object
189
+ finding = IntegrationFinding(
190
+ asset_identifier=asset_identifier,
191
+ control_labels=[],
192
+ category="CIS Benchmark",
193
+ plugin_name="", # Will be set by parser
194
+ title="", # Will be set by parser
195
+ description="", # Will be set by parser
196
+ severity=regscale_models.IssueSeverity.NotAssigned, # Will be set by parser
197
+ status=regscale_models.IssueStatus.Open, # Will be set by parser
198
+ first_seen=self.scan_date,
199
+ last_seen=self.scan_date,
200
+ )
201
+
202
+ # Parse using CIS parser
203
+ finding = parse_cis_compliance_result(compliance_data, finding)
204
+
205
+ # Filter by severity if configured
206
+ if finding.severity not in get_filtered_severities():
207
+ return None
208
+
209
+ return finding
210
+
211
+ except Exception as e:
212
+ logger.error(f"Error parsing CIS compliance finding: {str(e)}", exc_info=True)
213
+ return None
214
+
215
+
216
+ class TenableSCCISChecklistIntegration(ScannerIntegration):
217
+ """
218
+ Tenable Security Center CIS Benchmark Checklist Integration.
219
+
220
+ This integration class fetches CIS benchmark compliance data from Tenable Security Center
221
+ using the analysis API and maps it to RegScale checklist items.
222
+
223
+ Attributes:
224
+ title: Integration title displayed in RegScale
225
+ type: Integration type (CHECKLIST for compliance data)
226
+ asset_identifier_field: Field name used for asset identification
227
+ """
228
+
229
+ title = "Tenable SC CIS Benchmarks"
230
+ type = ScannerIntegrationType.CHECKLIST
231
+ asset_identifier_field = "tenableId"
232
+
233
+ # Map CIS compliance status to RegScale checklist status
234
+ checklist_status_map = {
235
+ "PASSED": regscale_models.ChecklistStatus.PASS,
236
+ "FAILED": regscale_models.ChecklistStatus.FAIL,
237
+ "WARNING": regscale_models.ChecklistStatus.NOT_REVIEWED,
238
+ "ERROR": regscale_models.ChecklistStatus.FAIL,
239
+ "NOT_APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
240
+ }
241
+
242
+ # Map severity levels (Tenable SC uses severity for compliance status)
243
+ finding_severity_map = {
244
+ "Info": regscale_models.IssueSeverity.NotAssigned, # Passed checks
245
+ "Low": regscale_models.IssueSeverity.Low,
246
+ "Medium": regscale_models.IssueSeverity.Moderate, # Manual/Warning checks
247
+ "High": regscale_models.IssueSeverity.High, # Failed checks
248
+ "Critical": regscale_models.IssueSeverity.Critical,
249
+ }
250
+
251
+ # CIS benchmark plugin IDs
252
+ CIS_PLUGIN_IDS = [
253
+ "21156", # Windows Compliance Checks
254
+ "19506", # Unix Compliance Checks
255
+ "33814", # Unix Compliance Checks (JSON)
256
+ "21745", # Policy compliance settings
257
+ ]
258
+
259
+ # CIS-related plugin families
260
+ CIS_PLUGIN_FAMILIES = [
261
+ "Policy Compliance",
262
+ "Windows : SCAP",
263
+ "SCAP Windows Compliance",
264
+ "UNIX Compliance Checks",
265
+ ]
266
+
267
+ def __init__(
268
+ self,
269
+ plan_id: int,
270
+ tenant_id: int = 1,
271
+ query_id: Optional[int] = None,
272
+ scan_date: Optional[str] = None,
273
+ batch_size: int = 1000,
274
+ cis_level: Optional[str] = None,
275
+ **kwargs: Any,
276
+ ) -> None:
277
+ """
278
+ Initialize the Tenable SC CIS Benchmark integration.
279
+
280
+ :param int plan_id: The RegScale security plan ID
281
+ :param int tenant_id: The RegScale tenant ID, defaults to 1
282
+ :param Optional[int] query_id: The Tenable SC query ID containing CIS compliance data
283
+ :param Optional[str] scan_date: The scan date for CIS assessment
284
+ :param int batch_size: Batch size for processing, defaults to 1000
285
+ :param Optional[str] cis_level: Filter by CIS level ("1" or "2")
286
+ :param Any kwargs: Additional keyword arguments
287
+ """
288
+ super().__init__(plan_id, tenant_id, **kwargs)
289
+ self.client = None
290
+ self.query_id = query_id
291
+ self.scan_date = scan_date or get_current_datetime()
292
+ self.batch_size = batch_size
293
+ self.cis_level = cis_level
294
+
295
+ def authenticate(self) -> None:
296
+ """Authenticate to Tenable Security Center."""
297
+ self.client = gen_tsc()
298
+
299
+ def fetch_assets(self, **kwargs: Any) -> Iterator[IntegrationAsset]:
300
+ """
301
+ Fetch assets from Tenable SC.
302
+
303
+ For CIS benchmark integration, assets are typically pre-existing in RegScale
304
+ and linked via asset identifiers in the compliance findings.
305
+
306
+ :param Any kwargs: Additional keyword arguments
307
+ :yields: IntegrationAsset objects
308
+ :return: Iterator of IntegrationAsset objects
309
+ :rtype: Iterator[IntegrationAsset]
310
+ """
311
+ # CIS benchmark findings reference existing assets
312
+ integration_assets = kwargs.get("integration_assets", [])
313
+ yield from integration_assets
314
+
315
+ def _should_skip_finding(self, vuln: dict) -> bool:
316
+ """
317
+ Check if a finding should be skipped based on severity.
318
+
319
+ :param dict vuln: The vulnerability data from Tenable SC
320
+ :return: True if the finding should be skipped, False otherwise
321
+ :rtype: bool
322
+ """
323
+ severity_name = vuln.get("severity", {}).get("name", "").lower()
324
+ return severity_name in ["info"] # Skip passed checks (Info severity)
325
+
326
+ def _matches_cis_level_filter(self, finding: IntegrationFinding) -> bool:
327
+ """
328
+ Check if a finding matches the CIS level filter.
329
+
330
+ :param IntegrationFinding finding: The finding to check
331
+ :return: True if the finding matches the filter or no filter is set, False otherwise
332
+ :rtype: bool
333
+ """
334
+ if not self.cis_level:
335
+ return True
336
+ return f"Level {self.cis_level}" in finding.baseline
337
+
338
+ def _log_progress(self, findings_count: int) -> None:
339
+ """
340
+ Log progress at regular intervals.
341
+
342
+ :param int findings_count: Current count of findings processed
343
+ """
344
+ if findings_count % _PROGRESS_LOG_INTERVAL == 0:
345
+ logger.info(f"Processed {findings_count} CIS compliance findings")
346
+
347
+ def fetch_findings(self, **kwargs: Any) -> Generator[IntegrationFinding, None, None]:
348
+ """
349
+ Fetch CIS benchmark compliance findings from Tenable SC.
350
+
351
+ Uses the Tenable SC analysis API to retrieve CIS benchmark compliance
352
+ check results and converts them to IntegrationFinding objects.
353
+
354
+ :param Any kwargs: Additional keyword arguments
355
+ :yields: IntegrationFinding objects
356
+ :return: Generator of IntegrationFinding objects
357
+ :rtype: Generator[IntegrationFinding, None, None]
358
+ """
359
+ logger.info("Fetching CIS benchmark compliance findings from Tenable SC...")
360
+
361
+ self.authenticate()
362
+
363
+ if not self.client:
364
+ raise ValueError("Tenable SC client not authenticated")
365
+
366
+ if not self.query_id:
367
+ raise ValueError("query_id is required for Tenable SC CIS integration")
368
+
369
+ # Query CIS compliance findings
370
+ try:
371
+ # Use analysis API with filters for CIS benchmarks
372
+ results = self.client.analysis.vulns(
373
+ ("benchmarkName", "=", "CIS"), # Filter for CIS benchmarks
374
+ ("pluginID", "=", ",".join(self.CIS_PLUGIN_IDS)), # CIS plugin IDs
375
+ tool="vulndetails",
376
+ query_id=self.query_id,
377
+ )
378
+
379
+ findings_count = 0
380
+ for vuln in results:
381
+ # Skip passed checks (Info severity)
382
+ if self._should_skip_finding(vuln):
383
+ continue
384
+
385
+ # Parse the finding
386
+ finding = self._parse_sc_finding(vuln)
387
+
388
+ # Filter by CIS level and yield if valid
389
+ if finding and self._matches_cis_level_filter(finding):
390
+ findings_count += 1
391
+ self._log_progress(findings_count)
392
+ yield finding
393
+
394
+ self.num_findings_to_process = findings_count
395
+ logger.info(f"Total CIS compliance findings processed: {findings_count}")
396
+
397
+ except Exception as e:
398
+ logger.error(f"Error fetching CIS compliance findings from Tenable SC: {str(e)}", exc_info=True)
399
+ raise
400
+
401
+ def _parse_sc_finding(self, vuln: dict) -> Optional[IntegrationFinding]:
402
+ """
403
+ Parse a Tenable SC vulnerability/compliance finding into an IntegrationFinding.
404
+
405
+ :param dict vuln: The vulnerability data from Tenable SC analysis API
406
+ :return: IntegrationFinding object or None if parsing fails
407
+ :rtype: Optional[IntegrationFinding]
408
+ """
409
+ try:
410
+ # Extract asset identifier
411
+ asset_identifier = vuln.get("dnsName") or vuln.get("ip", "")
412
+
413
+ if not asset_identifier:
414
+ logger.warning("SC finding missing asset identifier, skipping")
415
+ return None
416
+
417
+ # Extract plugin information
418
+ plugin_id = vuln.get("pluginID", "")
419
+ plugin_output = vuln.get("pluginText", "")
420
+
421
+ # Create base finding object
422
+ finding = IntegrationFinding(
423
+ asset_identifier=asset_identifier,
424
+ control_labels=[],
425
+ category="CIS Benchmark",
426
+ plugin_name="", # Will be set by parser
427
+ title="", # Will be set by parser
428
+ description="", # Will be set by parser
429
+ severity=regscale_models.IssueSeverity.NotAssigned, # Will be set by parser
430
+ status=regscale_models.IssueStatus.Open, # Will be set by parser
431
+ plugin_id=plugin_id,
432
+ first_seen=self.scan_date,
433
+ last_seen=self.scan_date,
434
+ )
435
+
436
+ # Parse using Tenable SC CIS parser
437
+ finding = parse_tenable_sc_cis_result(plugin_output, finding)
438
+
439
+ # Filter by severity if configured
440
+ if finding.severity not in get_filtered_severities():
441
+ return None
442
+
443
+ return finding
444
+
445
+ except Exception as e:
446
+ logger.error(f"Error parsing Tenable SC CIS finding: {str(e)}", exc_info=True)
447
+ return None
@@ -26,6 +26,10 @@ from regscale.core.app.utils.app_utils import (
26
26
  from regscale.integrations.commercial.nessus.nessus_utils import get_cpe_file
27
27
  from regscale.integrations.commercial.nessus.scanner import NessusIntegration
28
28
  from regscale.integrations.commercial.tenablev2.authenticate import gen_tsc, gen_tio
29
+ from regscale.integrations.commercial.tenablev2.cis_scanner import (
30
+ TenableIOCISChecklistIntegration,
31
+ TenableSCCISChecklistIntegration,
32
+ )
29
33
  from regscale.integrations.commercial.tenablev2.jsonl_scanner import TenableSCJsonlScanner
30
34
  from regscale.integrations.commercial.tenablev2.sc_scanner import SCIntegration
31
35
  from regscale.integrations.commercial.tenablev2.variables import TenableVariables
@@ -806,12 +810,149 @@ def sync_compliance_data(regscale_ssp_id: int, catalog_id: int, framework: str,
806
810
  sync_compliance_data(ssp_id=regscale_ssp_id, catalog_id=catalog_id, framework=framework, offline=offline)
807
811
 
808
812
 
809
- # Add import_nessus to __all__ exports at the end of the file
813
+ @io.command(name="sync_cis_checklist")
814
+ @regscale_id(help="RegScale security plan ID to create CIS checklist findings under.")
815
+ @click.option(
816
+ "--cis_level",
817
+ type=click.Choice(["1", "2"]),
818
+ default="1",
819
+ help="CIS benchmark level to sync (1 or 2). Default: 1",
820
+ )
821
+ @click.option(
822
+ "--audit_file_filter",
823
+ type=click.STRING,
824
+ default=None,
825
+ help="Filter by audit file name pattern (e.g., 'CIS_Ubuntu_Linux').",
826
+ )
827
+ @click.option(
828
+ "--tags",
829
+ type=click.STRING,
830
+ multiple=True,
831
+ default=None,
832
+ help="Filter by Tenable.io tags (can specify multiple times).",
833
+ )
834
+ def sync_cis_checklist_io(regscale_id: int, cis_level: str, audit_file_filter: Optional[str], tags: Optional[tuple]):
835
+ """
836
+ Sync CIS benchmark compliance checklist from Tenable.io to RegScale.
837
+
838
+ This command fetches CIS benchmark compliance data from Tenable.io
839
+ and creates checklist findings in RegScale for failed or warning compliance checks.
840
+
841
+ Examples:
842
+ # Sync CIS Level 1 findings
843
+ regscale tenable io sync_cis_checklist --regscale-id 123
844
+
845
+ # Sync CIS Level 2 findings for Ubuntu
846
+ regscale tenable io sync_cis_checklist --regscale-id 123 --cis-level 2 --audit-file-filter "Ubuntu"
847
+
848
+ # Sync with tag filtering
849
+ regscale tenable io sync_cis_checklist --regscale-id 123 --tags production --tags linux
850
+ """
851
+ try:
852
+ logger.info("Starting Tenable.io CIS checklist sync...")
853
+ logger.info("CIS Level: %s", cis_level)
854
+ if audit_file_filter:
855
+ logger.info("Audit File Filter: %s", audit_file_filter)
856
+ if tags:
857
+ logger.info("Tags: %s", ", ".join(tags))
858
+
859
+ # Initialize the CIS scanner
860
+ scanner = TenableIOCISChecklistIntegration(
861
+ plan_id=regscale_id,
862
+ cis_level=cis_level,
863
+ audit_file_filter=audit_file_filter,
864
+ tags=list(tags) if tags else None,
865
+ )
866
+
867
+ # Fetch and sync findings
868
+ findings_count = 0
869
+ assets_count = 0
870
+
871
+ for _ in scanner.fetch_findings():
872
+ findings_count += 1
873
+
874
+ for _ in scanner.fetch_assets():
875
+ assets_count += 1
876
+
877
+ logger.info("Synced %d CIS findings and %d assets", findings_count, assets_count)
878
+ logger.info("Tenable.io CIS checklist sync completed successfully.")
879
+
880
+ except Exception as e:
881
+ logger.error("Error syncing Tenable.io CIS checklist: %s", str(e), exc_info=True)
882
+ raise
883
+
884
+
885
+ @sc.command(name="sync_cis_checklist")
886
+ @regscale_id(help="RegScale security plan ID to create CIS checklist findings under.")
887
+ @click.option(
888
+ "--query_id",
889
+ type=click.INT,
890
+ required=True,
891
+ help="Tenable SC Query ID to retrieve CIS compliance data from.",
892
+ prompt="Enter Tenable SC Query ID for CIS data",
893
+ )
894
+ @click.option(
895
+ "--cis_level",
896
+ type=click.Choice(["1", "2"]),
897
+ default="1",
898
+ help="CIS benchmark level to sync (1 or 2). Default: 1",
899
+ )
900
+ def sync_cis_checklist_sc(regscale_id: int, query_id: int, cis_level: str):
901
+ """
902
+ Sync CIS benchmark compliance checklist from Tenable SC to RegScale.
903
+
904
+ This command fetches CIS benchmark compliance data from Tenable Security Center
905
+ using an analysis query and creates checklist findings in RegScale.
906
+
907
+ The query should be configured in Tenable SC to return CIS compliance plugin results.
908
+ Common CIS plugin IDs: 21156, 24760
909
+
910
+ Examples:
911
+ # Sync CIS Level 1 findings from query 42
912
+ regscale tenable sc sync_cis_checklist --regscale-id 123 --query-id 42
913
+
914
+ # Sync CIS Level 2 findings
915
+ regscale tenable sc sync_cis_checklist --regscale-id 123 --query-id 42 --cis-level 2
916
+ """
917
+ try:
918
+ logger.info("Starting Tenable SC CIS checklist sync...")
919
+ logger.info("Query ID: %d", query_id)
920
+ logger.info("CIS Level: %s", cis_level)
921
+
922
+ # Initialize the CIS scanner
923
+ scanner = TenableSCCISChecklistIntegration(
924
+ plan_id=regscale_id,
925
+ query_id=query_id,
926
+ cis_level=cis_level,
927
+ )
928
+
929
+ # Fetch and sync findings
930
+ findings_count = 0
931
+ assets_count = 0
932
+
933
+ for _ in scanner.fetch_findings():
934
+ findings_count += 1
935
+
936
+ for _ in scanner.fetch_assets():
937
+ assets_count += 1
938
+
939
+ logger.info("Synced %d CIS findings and %d assets", findings_count, assets_count)
940
+ logger.info("Tenable SC CIS checklist sync completed successfully.")
941
+
942
+ except Exception as e:
943
+ logger.error("Error syncing Tenable SC CIS checklist: %s", str(e), exc_info=True)
944
+ raise
945
+
946
+
947
+ # Add exports at the end of the file
810
948
  __all__ = [
811
949
  "tenable",
812
950
  "sc",
951
+ "io",
813
952
  "nessus",
814
953
  "import_nessus",
815
954
  "sync_vulns",
816
955
  "sync_jsonl",
956
+ "sync_cis_checklist_io",
957
+ "sync_cis_checklist_sc",
817
958
  ]
@@ -171,7 +171,6 @@ class TenableIntegration(ScannerIntegration):
171
171
  ip_address=self.get_all_ip_addresses(node),
172
172
  mac_address=self.get_all_mac_addresses(node),
173
173
  fqdn=", ".join(node.get("fqdns", [])),
174
- component_names=node.get("agent_names", []),
175
174
  operating_system=", ".join(node.get("operating_systems", [])),
176
175
  serial_number=node.get("bios_uuid", ""),
177
176
  notes=self.generate_notes(node),