regscale-cli 6.26.0.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.
- regscale/_version.py +1 -1
- regscale/core/app/application.py +1 -1
- regscale/core/app/internal/evidence.py +419 -2
- regscale/dev/code_gen.py +24 -20
- regscale/integrations/commercial/jira.py +367 -126
- regscale/integrations/commercial/qualys/__init__.py +7 -8
- regscale/integrations/commercial/qualys/scanner.py +8 -3
- regscale/integrations/commercial/synqly/assets.py +17 -0
- regscale/integrations/commercial/synqly/vulnerabilities.py +45 -28
- regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
- regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
- regscale/integrations/commercial/tenablev2/commands.py +142 -1
- regscale/integrations/commercial/tenablev2/scanner.py +0 -1
- regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
- regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
- regscale/integrations/commercial/wizv2/click.py +44 -59
- regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
- regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
- regscale/integrations/commercial/wizv2/compliance_report.py +10 -9
- regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
- regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +3 -3
- regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +1 -17
- regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
- regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +5 -9
- regscale/integrations/commercial/wizv2/issue.py +1 -1
- regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
- regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
- regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
- regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
- regscale/integrations/commercial/wizv2/reports.py +1 -1
- regscale/integrations/commercial/wizv2/sbom.py +1 -1
- regscale/integrations/commercial/wizv2/scanner.py +40 -100
- regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
- regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
- regscale/integrations/commercial/wizv2/variables.py +89 -3
- regscale/integrations/compliance_integration.py +0 -46
- regscale/integrations/control_matcher.py +22 -3
- regscale/integrations/due_date_handler.py +14 -8
- regscale/integrations/public/fedramp/docx_parser.py +10 -1
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +393 -340
- regscale/integrations/public/fedramp/fedramp_five.py +1 -1
- regscale/integrations/scanner_integration.py +127 -57
- regscale/models/integration_models/cisa_kev_data.json +132 -9
- regscale/models/integration_models/qualys.py +3 -4
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +24 -7
- regscale/models/integration_models/synqly_models/synqly_model.py +8 -1
- regscale/models/regscale_models/control_implementation.py +1 -1
- regscale/models/regscale_models/issue.py +0 -1
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/METADATA +1 -17
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/RECORD +93 -60
- tests/regscale/integrations/commercial/test_jira.py +481 -91
- tests/regscale/integrations/commercial/test_wiz.py +96 -200
- tests/regscale/integrations/commercial/wizv2/__init__.py +1 -1
- tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
- tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
- tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
- tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
- tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
- tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
- tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
- tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
- tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
- tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
- tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
- tests/regscale/integrations/commercial/wizv2/test_issue.py +1 -1
- tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
- tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
- tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
- tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +1 -1
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +72 -29
- tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2.py +946 -78
- tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +97 -202
- tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
- tests/regscale/integrations/public/test_fedramp.py +301 -0
- tests/regscale/integrations/test_control_matcher.py +83 -0
- regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3543
- tests/regscale/integrations/commercial/wizv2/test_wiz_policy_compliance.py +0 -750
- /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.26.0.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
|
-
|
|
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),
|