regscale-cli 6.20.4.1__py3-none-any.whl → 6.20.6.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/__init__.py +1 -1
- regscale/_version.py +39 -0
- regscale/core/app/internal/__init__.py +13 -0
- regscale/core/app/internal/model_editor.py +3 -3
- regscale/core/app/internal/set_permissions.py +173 -0
- regscale/core/app/utils/file_utils.py +11 -1
- regscale/core/app/utils/regscale_utils.py +34 -129
- regscale/core/utils/date.py +86 -30
- regscale/integrations/commercial/defender.py +3 -0
- regscale/integrations/commercial/qualys/__init__.py +40 -14
- regscale/integrations/commercial/qualys/containers.py +324 -0
- regscale/integrations/commercial/qualys/scanner.py +203 -8
- regscale/integrations/commercial/synqly/edr.py +10 -0
- regscale/integrations/commercial/wizv2/click.py +11 -7
- regscale/integrations/commercial/wizv2/constants.py +28 -0
- regscale/integrations/commercial/wizv2/issue.py +3 -2
- regscale/integrations/commercial/wizv2/parsers.py +23 -0
- regscale/integrations/commercial/wizv2/scanner.py +89 -30
- regscale/integrations/commercial/wizv2/utils.py +208 -75
- regscale/integrations/commercial/wizv2/variables.py +2 -1
- regscale/integrations/commercial/wizv2/wiz_auth.py +3 -3
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +98 -20
- regscale/integrations/public/fedramp/fedramp_docx.py +2 -3
- regscale/integrations/scanner_integration.py +7 -2
- regscale/models/integration_models/cisa_kev_data.json +187 -5
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/regscale_models/__init__.py +2 -0
- regscale/models/regscale_models/asset.py +1 -1
- regscale/models/regscale_models/catalog.py +16 -0
- regscale/models/regscale_models/file.py +2 -1
- regscale/models/regscale_models/form_field_value.py +59 -1
- regscale/models/regscale_models/issue.py +47 -0
- regscale/models/regscale_models/modules.py +88 -1
- regscale/models/regscale_models/organization.py +30 -0
- regscale/models/regscale_models/regscale_model.py +20 -6
- regscale/models/regscale_models/security_control.py +47 -0
- regscale/models/regscale_models/security_plan.py +32 -0
- regscale/models/regscale_models/vulnerability.py +3 -3
- regscale/models/regscale_models/vulnerability_mapping.py +2 -2
- regscale/regscale.py +2 -0
- {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.6.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.6.0.dist-info}/RECORD +49 -44
- tests/fixtures/test_fixture.py +33 -4
- tests/regscale/core/test_app.py +53 -32
- tests/regscale/test_init.py +94 -0
- {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.6.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.6.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.6.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.6.0.dist-info}/top_level.txt +0 -0
|
@@ -5,10 +5,11 @@ import json
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
7
|
import re
|
|
8
|
-
from typing import Any, Dict, Iterator, List, Optional, Union
|
|
8
|
+
from typing import Any, Dict, Iterator, List, Optional, Union, Tuple
|
|
9
9
|
|
|
10
10
|
from regscale.core.app.utils.app_utils import check_file_path, get_current_datetime
|
|
11
11
|
from regscale.core.utils import get_base_protocol_from_port
|
|
12
|
+
from regscale.core.utils.date import format_to_regscale_iso
|
|
12
13
|
from regscale.integrations.commercial.wizv2.constants import (
|
|
13
14
|
INVENTORY_FILE_PATH,
|
|
14
15
|
INVENTORY_QUERY,
|
|
@@ -19,7 +20,6 @@ from regscale.integrations.commercial.wizv2.parsers import (
|
|
|
19
20
|
collect_components_to_create,
|
|
20
21
|
fetch_wiz_data,
|
|
21
22
|
get_disk_storage,
|
|
22
|
-
get_ip_address_from_props,
|
|
23
23
|
get_latest_version,
|
|
24
24
|
get_network_info,
|
|
25
25
|
get_product_ids,
|
|
@@ -101,7 +101,7 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
101
101
|
:yield: IntegrationFinding objects
|
|
102
102
|
:rtype: Iterator[IntegrationFinding]
|
|
103
103
|
"""
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
project_id = kwargs.get("wiz_project_id")
|
|
106
106
|
if not project_id:
|
|
107
107
|
raise ValueError("Wiz project ID is required")
|
|
@@ -137,7 +137,7 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
137
137
|
"""
|
|
138
138
|
for node in nodes:
|
|
139
139
|
if finding := self.parse_finding(node, vulnerability_type):
|
|
140
|
-
self.num_findings_to_process
|
|
140
|
+
self.num_findings_to_process = (self.num_findings_to_process or 0) + 1
|
|
141
141
|
yield finding
|
|
142
142
|
|
|
143
143
|
@classmethod
|
|
@@ -151,6 +151,25 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
151
151
|
"""
|
|
152
152
|
return cls.finding_severity_map.get(severity.capitalize(), regscale_models.IssueSeverity.Low)
|
|
153
153
|
|
|
154
|
+
def process_comments(self, comments_dict: Dict) -> Optional[str]:
|
|
155
|
+
"""
|
|
156
|
+
Processes comments from Wiz findings to match RegScale's comment format.
|
|
157
|
+
|
|
158
|
+
:param Dict comments_dict: The comments from the Wiz finding
|
|
159
|
+
:return: If available the Processed comments in RegScale format
|
|
160
|
+
:rtype: Optional[str]
|
|
161
|
+
"""
|
|
162
|
+
result = None
|
|
163
|
+
|
|
164
|
+
if comments := comments_dict.get("comments", {}).get("edges", []):
|
|
165
|
+
formatted_comments = [
|
|
166
|
+
f"{edge.get('node', {}).get('author', {}).get('name', 'Unknown')}: {edge.get('node', {}).get('body', 'No comment')}"
|
|
167
|
+
for edge in comments
|
|
168
|
+
]
|
|
169
|
+
# Join with newlines
|
|
170
|
+
result = "\n".join(formatted_comments)
|
|
171
|
+
return result
|
|
172
|
+
|
|
154
173
|
def parse_finding(
|
|
155
174
|
self, node: Dict[str, Any], vulnerability_type: WizVulnerabilityType
|
|
156
175
|
) -> Optional[IntegrationFinding]:
|
|
@@ -167,7 +186,10 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
167
186
|
if not asset_id:
|
|
168
187
|
return None
|
|
169
188
|
|
|
189
|
+
first_seen = node.get("firstDetectedAt") or node.get("firstSeenAt") or get_current_datetime()
|
|
190
|
+
first_seen = format_to_regscale_iso(first_seen)
|
|
170
191
|
severity = self.get_issue_severity(node.get("severity", "Low"))
|
|
192
|
+
due_date = regscale_models.Issue.get_due_date(severity, self.app.config, "wiz", first_seen)
|
|
171
193
|
|
|
172
194
|
status = self.map_status_to_issue_status(node.get("status", "Open"))
|
|
173
195
|
name: str = node.get("name", "")
|
|
@@ -177,6 +199,9 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
177
199
|
else node.get("cve", name)
|
|
178
200
|
)
|
|
179
201
|
|
|
202
|
+
comments_dict = node.get("commentThread", {})
|
|
203
|
+
formatted_comments = self.process_comments(comments_dict)
|
|
204
|
+
|
|
180
205
|
return IntegrationFinding(
|
|
181
206
|
control_labels=[],
|
|
182
207
|
category="Wiz Vulnerability",
|
|
@@ -186,8 +211,11 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
186
211
|
status=status,
|
|
187
212
|
asset_identifier=asset_id,
|
|
188
213
|
external_id=f"{node.get('sourceRule', {'id': cve}).get('id')}",
|
|
189
|
-
first_seen=
|
|
190
|
-
|
|
214
|
+
first_seen=first_seen,
|
|
215
|
+
date_created=first_seen,
|
|
216
|
+
last_seen=format_to_regscale_iso(
|
|
217
|
+
node.get("lastDetectedAt") or node.get("analyzedAt") or get_current_datetime()
|
|
218
|
+
),
|
|
191
219
|
remediation=node.get("description", ""),
|
|
192
220
|
cvss_score=node.get("score"),
|
|
193
221
|
cve=cve,
|
|
@@ -195,6 +223,11 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
195
223
|
cvss_v3_base_score=node.get("score"),
|
|
196
224
|
source_rule_id=node.get("sourceRule", {}).get("id"),
|
|
197
225
|
vulnerability_type=vulnerability_type.value,
|
|
226
|
+
due_date=due_date,
|
|
227
|
+
date_last_updated=format_to_regscale_iso(get_current_datetime()),
|
|
228
|
+
identification="Vulnerability Assessment",
|
|
229
|
+
comments=formatted_comments,
|
|
230
|
+
poam_comments=formatted_comments,
|
|
198
231
|
)
|
|
199
232
|
except (KeyError, TypeError, ValueError) as e:
|
|
200
233
|
logger.error("Error parsing Wiz finding: %s", str(e), exc_info=True)
|
|
@@ -222,9 +255,9 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
222
255
|
:yields: Iterator[IntegrationAsset]
|
|
223
256
|
"""
|
|
224
257
|
self.authenticate(kwargs.get("client_id"), kwargs.get("client_secret"))
|
|
225
|
-
wiz_project_id = kwargs.get("wiz_project_id")
|
|
258
|
+
wiz_project_id: str = kwargs.get("wiz_project_id", "")
|
|
226
259
|
logger.info("Fetching Wiz assets...")
|
|
227
|
-
filter_by_override = kwargs.get("filter_by_override") or WizVariables.wizInventoryFilterBy
|
|
260
|
+
filter_by_override: Dict[str, Any] = kwargs.get("filter_by_override") or WizVariables.wizInventoryFilterBy or {}
|
|
228
261
|
filter_by = self.get_filter_by(filter_by_override, wiz_project_id)
|
|
229
262
|
|
|
230
263
|
variables = self.get_variables()
|
|
@@ -257,6 +290,32 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
257
290
|
filter_by["updatedAt"] = {"after": WizVariables.wizLastInventoryPull} # type: ignore
|
|
258
291
|
return filter_by
|
|
259
292
|
|
|
293
|
+
def get_software_details(
|
|
294
|
+
self, wiz_entity_properties: Dict, node: Dict[str, Any], software_name_dict: Dict[str, str], name: str
|
|
295
|
+
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
296
|
+
"""
|
|
297
|
+
Gets the software version, vendor, and name from the Wiz entity properties and node.
|
|
298
|
+
Handles container images differently by extracting the version and name from the image tags.
|
|
299
|
+
:param Dict wiz_entity_properties: The properties of the Wiz entity
|
|
300
|
+
:param Dict node: The Wiz node containing the entity
|
|
301
|
+
:param Dict software_name_dict: Dictionary containing software name and vendor
|
|
302
|
+
:param str name: The name of the software or container image
|
|
303
|
+
:return: A tuple containing software_version, software_vendor, and software_name
|
|
304
|
+
:rtype: Tuple[Optional[str], Optional[str], Optional[str]]
|
|
305
|
+
"""
|
|
306
|
+
if node.get("type", "") == "CONTAINER_IMAGE":
|
|
307
|
+
software_version = handle_container_image_version(
|
|
308
|
+
image_tags=wiz_entity_properties.get("imageTags", []), name=name
|
|
309
|
+
)
|
|
310
|
+
software_name = name.split(":")[0].split("/")[-1] if name else ""
|
|
311
|
+
software_vendor = name.split(":")[0].split("/")[1] if len(name.split(":")[0].split("/")) > 1 else None
|
|
312
|
+
else:
|
|
313
|
+
software_version = self.get_software_version(wiz_entity_properties, node)
|
|
314
|
+
software_name = self.get_software_name(software_name_dict, wiz_entity_properties, node)
|
|
315
|
+
software_vendor = self.get_software_vendor(software_name_dict, wiz_entity_properties, node)
|
|
316
|
+
|
|
317
|
+
return software_version, software_vendor, software_name
|
|
318
|
+
|
|
260
319
|
def parse_asset(self, node: Dict[str, Any]) -> Optional[IntegrationAsset]:
|
|
261
320
|
"""
|
|
262
321
|
Parses Wiz assets
|
|
@@ -272,23 +331,20 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
272
331
|
return None
|
|
273
332
|
|
|
274
333
|
wiz_entity_properties = wiz_entity.get("properties", {})
|
|
334
|
+
is_public = False
|
|
335
|
+
if public_exposures := wiz_entity.get("publicExposures"):
|
|
336
|
+
if exposure_count := public_exposures.get("totalCount"):
|
|
337
|
+
is_public = exposure_count > 0
|
|
338
|
+
|
|
275
339
|
network_dict = get_network_info(wiz_entity_properties)
|
|
276
340
|
handle_provider_dict = handle_provider(wiz_entity_properties)
|
|
277
341
|
software_name_dict = get_software_name_from_cpe(wiz_entity_properties, name)
|
|
278
342
|
software_list = self.create_name_version_dict(wiz_entity_properties.get("installedPackages", []))
|
|
279
|
-
|
|
280
343
|
ports_and_protocols = self.get_ports_and_protocols(wiz_entity_properties)
|
|
281
344
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
)
|
|
286
|
-
software_name = name.split(":")[0].split("/")[-1] if name else ""
|
|
287
|
-
software_vendor = name.split(":")[0].split("/")[1] if len(name.split(":")[0].split("/")) > 1 else None
|
|
288
|
-
else:
|
|
289
|
-
software_version = self.get_software_version(wiz_entity_properties, node)
|
|
290
|
-
software_name = self.get_software_name(software_name_dict, wiz_entity_properties, node)
|
|
291
|
-
software_vendor = self.get_software_vendor(software_name_dict, wiz_entity_properties, node)
|
|
345
|
+
software_version, software_vendor, software_name = self.get_software_details(
|
|
346
|
+
wiz_entity_properties, node, software_name_dict, name
|
|
347
|
+
)
|
|
292
348
|
|
|
293
349
|
if WizVariables.useWizHardwareAssetTypes and node.get("graphEntity", {}).get("technologies", []):
|
|
294
350
|
technologies = node.get("graphEntity", {}).get("technologies", [])
|
|
@@ -312,7 +368,8 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
312
368
|
date_last_updated=wiz_entity.get("lastSeen", ""),
|
|
313
369
|
management_type=handle_management_type(wiz_entity_properties),
|
|
314
370
|
status=self.map_wiz_status(wiz_entity_properties.get("status")),
|
|
315
|
-
ip_address=
|
|
371
|
+
ip_address=network_dict.get("ip4_address"),
|
|
372
|
+
ipv6_address=network_dict.get("ip6_address"),
|
|
316
373
|
software_vendor=software_vendor,
|
|
317
374
|
software_version=software_version,
|
|
318
375
|
software_name=software_name,
|
|
@@ -321,10 +378,10 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
321
378
|
model=wiz_entity_properties.get("nativeType"),
|
|
322
379
|
manufacturer=wiz_entity_properties.get("cloudPlatform"),
|
|
323
380
|
serial_number=get_product_ids(wiz_entity_properties),
|
|
324
|
-
is_public_facing=
|
|
325
|
-
azure_identifier=handle_provider_dict.get("azureIdentifier"),
|
|
381
|
+
is_public_facing=is_public,
|
|
382
|
+
azure_identifier=handle_provider_dict.get("azureIdentifier", ""),
|
|
326
383
|
mac_address=wiz_entity_properties.get("macAddress"),
|
|
327
|
-
fqdn=
|
|
384
|
+
fqdn=network_dict.get("dns") or wiz_entity_properties.get("dnsName"),
|
|
328
385
|
disk_storage=get_disk_storage(wiz_entity_properties) or 0,
|
|
329
386
|
cpu=pull_resource_info_from_props(wiz_entity_properties)[1] or 0,
|
|
330
387
|
ram=pull_resource_info_from_props(wiz_entity_properties)[0] or 0,
|
|
@@ -333,9 +390,9 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
333
390
|
end_of_life_date=wiz_entity_properties.get("versionEndOfLifeDate"),
|
|
334
391
|
vlan_id=wiz_entity_properties.get("zone"),
|
|
335
392
|
uri=network_dict.get("url"),
|
|
336
|
-
aws_identifier=handle_provider_dict.get("awsIdentifier"),
|
|
337
|
-
google_identifier=handle_provider_dict.get("googleIdentifier"),
|
|
338
|
-
other_cloud_identifier=handle_provider_dict.get("otherCloudIdentifier"),
|
|
393
|
+
aws_identifier=handle_provider_dict.get("awsIdentifier", ""),
|
|
394
|
+
google_identifier=handle_provider_dict.get("googleIdentifier", ""),
|
|
395
|
+
other_cloud_identifier=handle_provider_dict.get("otherCloudIdentifier", ""),
|
|
339
396
|
patch_level=get_latest_version(wiz_entity_properties),
|
|
340
397
|
cpe=wiz_entity_properties.get("cpe"),
|
|
341
398
|
component_names=collect_components_to_create([node], []),
|
|
@@ -374,7 +431,7 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
374
431
|
:return: Software vendor
|
|
375
432
|
:rtype: Optional[str]
|
|
376
433
|
"""
|
|
377
|
-
if map_category(node.get("type")) == regscale_models.AssetCategory.Software:
|
|
434
|
+
if map_category(node.get("type", "")) == regscale_models.AssetCategory.Software:
|
|
378
435
|
return software_name_dict.get("software_vendor") or wiz_entity_properties.get("cloudPlatform")
|
|
379
436
|
return None
|
|
380
437
|
|
|
@@ -388,8 +445,8 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
388
445
|
:return: Software version
|
|
389
446
|
:rtype: Optional[str]
|
|
390
447
|
"""
|
|
391
|
-
if map_category(node.get("type")) == regscale_models.AssetCategory.Software:
|
|
392
|
-
return handle_software_version(wiz_entity_properties, map_category(node.get("type"))) or "1.0"
|
|
448
|
+
if map_category(node.get("type", "")) == regscale_models.AssetCategory.Software:
|
|
449
|
+
return handle_software_version(wiz_entity_properties, map_category(node.get("type", ""))) or "1.0"
|
|
393
450
|
return None
|
|
394
451
|
|
|
395
452
|
@staticmethod
|
|
@@ -403,7 +460,7 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
403
460
|
:return: Software name
|
|
404
461
|
:rtype: Optional[str]
|
|
405
462
|
"""
|
|
406
|
-
if map_category(node.get("type")) == regscale_models.AssetCategory.Software:
|
|
463
|
+
if map_category(node.get("type", "")) == regscale_models.AssetCategory.Software:
|
|
407
464
|
return software_name_dict.get("software_name") or wiz_entity_properties.get("nativeType")
|
|
408
465
|
return None
|
|
409
466
|
|
|
@@ -454,6 +511,8 @@ class WizVulnerabilityIntegration(ScannerIntegration):
|
|
|
454
511
|
else:
|
|
455
512
|
logger.info("File %s does not exist. Fetching new data...", file_path)
|
|
456
513
|
|
|
514
|
+
self.authenticate(WizVariables.wizClientId, WizVariables.wizClientSecret)
|
|
515
|
+
|
|
457
516
|
if not self.wiz_token:
|
|
458
517
|
raise ValueError("Wiz token is not set. Please authenticate first.")
|
|
459
518
|
|