regscale-cli 6.20.5.0__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.

Files changed (32) hide show
  1. regscale/__init__.py +1 -1
  2. regscale/_version.py +39 -0
  3. regscale/core/app/internal/__init__.py +13 -0
  4. regscale/core/app/internal/set_permissions.py +173 -0
  5. regscale/core/app/utils/file_utils.py +11 -1
  6. regscale/core/app/utils/regscale_utils.py +1 -133
  7. regscale/core/utils/date.py +62 -29
  8. regscale/integrations/commercial/wizv2/click.py +9 -5
  9. regscale/integrations/commercial/wizv2/constants.py +15 -0
  10. regscale/integrations/commercial/wizv2/parsers.py +23 -0
  11. regscale/integrations/commercial/wizv2/scanner.py +84 -29
  12. regscale/integrations/commercial/wizv2/utils.py +91 -4
  13. regscale/integrations/commercial/wizv2/variables.py +2 -1
  14. regscale/integrations/commercial/wizv2/wiz_auth.py +3 -3
  15. regscale/integrations/public/fedramp/fedramp_docx.py +2 -3
  16. regscale/integrations/scanner_integration.py +7 -2
  17. regscale/models/integration_models/cisa_kev_data.json +50 -5
  18. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  19. regscale/models/regscale_models/__init__.py +2 -0
  20. regscale/models/regscale_models/asset.py +1 -1
  21. regscale/models/regscale_models/modules.py +88 -1
  22. regscale/models/regscale_models/regscale_model.py +7 -1
  23. regscale/models/regscale_models/vulnerability.py +3 -3
  24. regscale/models/regscale_models/vulnerability_mapping.py +2 -2
  25. regscale/regscale.py +2 -0
  26. {regscale_cli-6.20.5.0.dist-info → regscale_cli-6.20.6.0.dist-info}/METADATA +1 -1
  27. {regscale_cli-6.20.5.0.dist-info → regscale_cli-6.20.6.0.dist-info}/RECORD +32 -29
  28. tests/regscale/test_init.py +94 -0
  29. {regscale_cli-6.20.5.0.dist-info → regscale_cli-6.20.6.0.dist-info}/LICENSE +0 -0
  30. {regscale_cli-6.20.5.0.dist-info → regscale_cli-6.20.6.0.dist-info}/WHEEL +0 -0
  31. {regscale_cli-6.20.5.0.dist-info → regscale_cli-6.20.6.0.dist-info}/entry_points.txt +0 -0
  32. {regscale_cli-6.20.5.0.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
- self.authenticate(kwargs.get("client_id"), kwargs.get("client_secret"))
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 += 1
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]:
@@ -168,6 +187,7 @@ class WizVulnerabilityIntegration(ScannerIntegration):
168
187
  return None
169
188
 
170
189
  first_seen = node.get("firstDetectedAt") or node.get("firstSeenAt") or get_current_datetime()
190
+ first_seen = format_to_regscale_iso(first_seen)
171
191
  severity = self.get_issue_severity(node.get("severity", "Low"))
172
192
  due_date = regscale_models.Issue.get_due_date(severity, self.app.config, "wiz", first_seen)
173
193
 
@@ -179,6 +199,9 @@ class WizVulnerabilityIntegration(ScannerIntegration):
179
199
  else node.get("cve", name)
180
200
  )
181
201
 
202
+ comments_dict = node.get("commentThread", {})
203
+ formatted_comments = self.process_comments(comments_dict)
204
+
182
205
  return IntegrationFinding(
183
206
  control_labels=[],
184
207
  category="Wiz Vulnerability",
@@ -190,7 +213,9 @@ class WizVulnerabilityIntegration(ScannerIntegration):
190
213
  external_id=f"{node.get('sourceRule', {'id': cve}).get('id')}",
191
214
  first_seen=first_seen,
192
215
  date_created=first_seen,
193
- last_seen=node.get("lastDetectedAt") or node.get("analyzedAt") or get_current_datetime(),
216
+ last_seen=format_to_regscale_iso(
217
+ node.get("lastDetectedAt") or node.get("analyzedAt") or get_current_datetime()
218
+ ),
194
219
  remediation=node.get("description", ""),
195
220
  cvss_score=node.get("score"),
196
221
  cve=cve,
@@ -199,6 +224,10 @@ class WizVulnerabilityIntegration(ScannerIntegration):
199
224
  source_rule_id=node.get("sourceRule", {}).get("id"),
200
225
  vulnerability_type=vulnerability_type.value,
201
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,
202
231
  )
203
232
  except (KeyError, TypeError, ValueError) as e:
204
233
  logger.error("Error parsing Wiz finding: %s", str(e), exc_info=True)
@@ -226,9 +255,9 @@ class WizVulnerabilityIntegration(ScannerIntegration):
226
255
  :yields: Iterator[IntegrationAsset]
227
256
  """
228
257
  self.authenticate(kwargs.get("client_id"), kwargs.get("client_secret"))
229
- wiz_project_id = kwargs.get("wiz_project_id")
258
+ wiz_project_id: str = kwargs.get("wiz_project_id", "")
230
259
  logger.info("Fetching Wiz assets...")
231
- 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 {}
232
261
  filter_by = self.get_filter_by(filter_by_override, wiz_project_id)
233
262
 
234
263
  variables = self.get_variables()
@@ -261,6 +290,32 @@ class WizVulnerabilityIntegration(ScannerIntegration):
261
290
  filter_by["updatedAt"] = {"after": WizVariables.wizLastInventoryPull} # type: ignore
262
291
  return filter_by
263
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
+
264
319
  def parse_asset(self, node: Dict[str, Any]) -> Optional[IntegrationAsset]:
265
320
  """
266
321
  Parses Wiz assets
@@ -276,23 +331,20 @@ class WizVulnerabilityIntegration(ScannerIntegration):
276
331
  return None
277
332
 
278
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
+
279
339
  network_dict = get_network_info(wiz_entity_properties)
280
340
  handle_provider_dict = handle_provider(wiz_entity_properties)
281
341
  software_name_dict = get_software_name_from_cpe(wiz_entity_properties, name)
282
342
  software_list = self.create_name_version_dict(wiz_entity_properties.get("installedPackages", []))
283
-
284
343
  ports_and_protocols = self.get_ports_and_protocols(wiz_entity_properties)
285
344
 
286
- if node.get("type", "") == "CONTAINER_IMAGE":
287
- software_version = handle_container_image_version(
288
- image_tags=wiz_entity_properties.get("imageTags", []), name=name
289
- )
290
- software_name = name.split(":")[0].split("/")[-1] if name else ""
291
- software_vendor = name.split(":")[0].split("/")[1] if len(name.split(":")[0].split("/")) > 1 else None
292
- else:
293
- software_version = self.get_software_version(wiz_entity_properties, node)
294
- software_name = self.get_software_name(software_name_dict, wiz_entity_properties, node)
295
- 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
+ )
296
348
 
297
349
  if WizVariables.useWizHardwareAssetTypes and node.get("graphEntity", {}).get("technologies", []):
298
350
  technologies = node.get("graphEntity", {}).get("technologies", [])
@@ -316,7 +368,8 @@ class WizVulnerabilityIntegration(ScannerIntegration):
316
368
  date_last_updated=wiz_entity.get("lastSeen", ""),
317
369
  management_type=handle_management_type(wiz_entity_properties),
318
370
  status=self.map_wiz_status(wiz_entity_properties.get("status")),
319
- ip_address=get_ip_address_from_props(wiz_entity_properties),
371
+ ip_address=network_dict.get("ip4_address"),
372
+ ipv6_address=network_dict.get("ip6_address"),
320
373
  software_vendor=software_vendor,
321
374
  software_version=software_version,
322
375
  software_name=software_name,
@@ -325,10 +378,10 @@ class WizVulnerabilityIntegration(ScannerIntegration):
325
378
  model=wiz_entity_properties.get("nativeType"),
326
379
  manufacturer=wiz_entity_properties.get("cloudPlatform"),
327
380
  serial_number=get_product_ids(wiz_entity_properties),
328
- is_public_facing=wiz_entity_properties.get("directlyInternetFacing", False),
329
- azure_identifier=handle_provider_dict.get("azureIdentifier"),
381
+ is_public_facing=is_public,
382
+ azure_identifier=handle_provider_dict.get("azureIdentifier", ""),
330
383
  mac_address=wiz_entity_properties.get("macAddress"),
331
- fqdn=wiz_entity_properties.get("dnsName") or network_dict.get("dns"),
384
+ fqdn=network_dict.get("dns") or wiz_entity_properties.get("dnsName"),
332
385
  disk_storage=get_disk_storage(wiz_entity_properties) or 0,
333
386
  cpu=pull_resource_info_from_props(wiz_entity_properties)[1] or 0,
334
387
  ram=pull_resource_info_from_props(wiz_entity_properties)[0] or 0,
@@ -337,9 +390,9 @@ class WizVulnerabilityIntegration(ScannerIntegration):
337
390
  end_of_life_date=wiz_entity_properties.get("versionEndOfLifeDate"),
338
391
  vlan_id=wiz_entity_properties.get("zone"),
339
392
  uri=network_dict.get("url"),
340
- aws_identifier=handle_provider_dict.get("awsIdentifier"),
341
- google_identifier=handle_provider_dict.get("googleIdentifier"),
342
- 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", ""),
343
396
  patch_level=get_latest_version(wiz_entity_properties),
344
397
  cpe=wiz_entity_properties.get("cpe"),
345
398
  component_names=collect_components_to_create([node], []),
@@ -378,7 +431,7 @@ class WizVulnerabilityIntegration(ScannerIntegration):
378
431
  :return: Software vendor
379
432
  :rtype: Optional[str]
380
433
  """
381
- if map_category(node.get("type")) == regscale_models.AssetCategory.Software:
434
+ if map_category(node.get("type", "")) == regscale_models.AssetCategory.Software:
382
435
  return software_name_dict.get("software_vendor") or wiz_entity_properties.get("cloudPlatform")
383
436
  return None
384
437
 
@@ -392,8 +445,8 @@ class WizVulnerabilityIntegration(ScannerIntegration):
392
445
  :return: Software version
393
446
  :rtype: Optional[str]
394
447
  """
395
- if map_category(node.get("type")) == regscale_models.AssetCategory.Software:
396
- 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"
397
450
  return None
398
451
 
399
452
  @staticmethod
@@ -407,7 +460,7 @@ class WizVulnerabilityIntegration(ScannerIntegration):
407
460
  :return: Software name
408
461
  :rtype: Optional[str]
409
462
  """
410
- if map_category(node.get("type")) == regscale_models.AssetCategory.Software:
463
+ if map_category(node.get("type", "")) == regscale_models.AssetCategory.Software:
411
464
  return software_name_dict.get("software_name") or wiz_entity_properties.get("nativeType")
412
465
  return None
413
466
 
@@ -458,6 +511,8 @@ class WizVulnerabilityIntegration(ScannerIntegration):
458
511
  else:
459
512
  logger.info("File %s does not exist. Fetching new data...", file_path)
460
513
 
514
+ self.authenticate(WizVariables.wizClientId, WizVariables.wizClientSecret)
515
+
461
516
  if not self.wiz_token:
462
517
  raise ValueError("Wiz token is not set. Please authenticate first.")
463
518
 
@@ -19,6 +19,7 @@ from pydantic import ValidationError
19
19
  from rich.progress import Progress, TaskID
20
20
 
21
21
  from regscale.core.app.api import Api
22
+ from regscale.core.app.application import Application
22
23
  from regscale.core.app.utils.app_utils import (
23
24
  error_and_exit,
24
25
  check_file_path,
@@ -50,6 +51,8 @@ from regscale.models import (
50
51
  ControlImplementation,
51
52
  Assessment,
52
53
  regscale_models,
54
+ ControlImplementationStatus,
55
+ ImplementationObjective,
53
56
  )
54
57
  from regscale.utils import PaginatedGraphQLClient
55
58
  from regscale.utils.decorators import deprecated
@@ -58,6 +61,31 @@ logger = logging.getLogger("regscale")
58
61
  compliance_job_progress = create_progress_object()
59
62
 
60
63
 
64
+ def is_report_expired(report_run_at: str, max_age_days: int) -> bool:
65
+ """
66
+ Check if a report is expired based on its run date
67
+
68
+ :param str report_run_at: Report run date in ISO format
69
+ :param int max_age_days: Maximum age in days
70
+ :return: True if report is expired, False otherwise
71
+ :rtype: bool
72
+ """
73
+ try:
74
+ run_date = datetime_obj(report_run_at)
75
+ if not run_date:
76
+ return True
77
+
78
+ # Convert to naive datetime for comparison
79
+ run_date_naive = run_date.replace(tzinfo=None)
80
+ current_date = datetime.datetime.now()
81
+ age_in_days = (current_date - run_date_naive).days
82
+
83
+ return age_in_days >= max_age_days
84
+ except (ValueError, TypeError):
85
+ # If we can't parse the date, consider it expired
86
+ return True
87
+
88
+
61
89
  def get_notes_from_wiz_props(wiz_entity_properties: Dict, external_id: str) -> str:
62
90
  """
63
91
  Get notes from wiz properties
@@ -418,15 +446,28 @@ def get_or_create_report_id(
418
446
  :param target_framework: Target framework name with underscores
419
447
  :return: Single report ID
420
448
  """
449
+ app = Application()
450
+ report_age_days = app.config.get("wizReportAge", 15)
421
451
  report_name = f"{target_framework}_project_{project_id}"
422
452
 
423
453
  # Check for existing report with exact name
424
454
  for report in existing_reports:
425
455
  if report.get("name") == report_name:
426
456
  logger.info(f"Found existing report '{report_name}' with ID {report['id']}")
427
- return report["id"]
428
457
 
429
- # Create new report if no exact match found
458
+ # Check if report is expired based on wizReportAge
459
+ run_at = report.get("lastRun", {}).get("runAt")
460
+
461
+ if run_at and is_report_expired(run_at, report_age_days):
462
+ logger.info(
463
+ f"Report '{report_name}' is expired (older than {report_age_days} days), will create new report"
464
+ )
465
+ break
466
+ else:
467
+ logger.info(f"Report '{report_name}' is still valid, using existing report")
468
+ return report["id"]
469
+
470
+ # Create new report if no valid existing report found
430
471
  try:
431
472
  framework_index = frameworks.index(target_framework)
432
473
  framework_id = wiz_frameworks[framework_index].get("id")
@@ -756,7 +797,7 @@ def _sync_compliance(
756
797
  compliance_job_progress.update(report_job, completed=True, advance=1)
757
798
 
758
799
  if catalog_id:
759
- logger.info("Fetching all Controls for catalog #%d...", catalog_id)
800
+ logger.info("Fetching all Controls for catalog #%s...", catalog_id)
760
801
  catalog = Catalog.get_with_all_details(catalog_id=catalog_id)
761
802
  controls = catalog.get("controls") if catalog else []
762
803
  else:
@@ -916,7 +957,7 @@ def create_report_assessment(filtered_results: List, reports: List, control_id:
916
957
  for report in reports:
917
958
  html_summary = format_dict_to_html(report.dict())
918
959
  if implementation:
919
- Assessment(
960
+ a = Assessment(
920
961
  leadAssessorId=implementation.createdById,
921
962
  title=f"Wiz compliance report assessment for {control_id}",
922
963
  assessmentType="Control Testing",
@@ -930,3 +971,49 @@ def create_report_assessment(filtered_results: List, reports: List, control_id:
930
971
  parentModule="controls",
931
972
  isPublic=True,
932
973
  ).create()
974
+ update_implementation_status(
975
+ implementation=implementation,
976
+ result=report.result,
977
+ )
978
+ logger.info(f"Created report assessment for {control_id}: {a.id}")
979
+
980
+
981
+ def update_implementation_status(implementation: ControlImplementation, result: str) -> ControlImplementation:
982
+ """
983
+ Update implementation status based on the report result
984
+
985
+ :param ControlImplementation implementation: Control Implementation object
986
+ :param str result: Report result
987
+ :return: Updated Control Implementation object
988
+ :rtype: ControlImplementation
989
+ """
990
+ objectives = ImplementationObjective.get_all_by_parent(
991
+ parent_module=implementation.get_module_slug(),
992
+ parent_id=implementation.id,
993
+ )
994
+ if objectives:
995
+ for objective in objectives:
996
+ objective.status = report_result_to_implementation_status(result)
997
+ objective.save()
998
+ logger.debug(f"Updated status for {objective.id}: {objective.status}")
999
+ else:
1000
+ implementation.objectives = []
1001
+ implementation.status = report_result_to_implementation_status(result)
1002
+ implementation.save()
1003
+ logger.info(f"Updated implementation status for {implementation.id}: {implementation.status}")
1004
+
1005
+
1006
+ def report_result_to_implementation_status(result: str) -> str:
1007
+ """
1008
+ Convert report result to implementation status
1009
+
1010
+ :param str result: Report result
1011
+ :return: Implementation status
1012
+ :rtype: str
1013
+ """
1014
+ if result == ComplianceCheckStatus.PASS.value:
1015
+ return ControlImplementationStatus.Implemented.value
1016
+ elif result == ComplianceCheckStatus.FAIL.value:
1017
+ return ControlImplementationStatus.InRemediation.value
1018
+ else:
1019
+ return ControlImplementationStatus.NotImplemented.value
@@ -31,7 +31,7 @@ class WizVariables(metaclass=RsVariablesMeta):
31
31
  "PRIVATE_LINK", "RAW_ACCESS_POLICY", "REGISTERED_DOMAIN", "RESOURCE_GROUP", "SECRET",
32
32
  "SECRET_CONTAINER", "SERVERLESS", "SERVERLESS_PACKAGE", "SERVICE_ACCOUNT", "SERVICE_CONFIGURATION",
33
33
  "STORAGE_ACCOUNT", "SUBNET", "SUBSCRIPTION", "VIRTUAL_DESKTOP", "VIRTUAL_MACHINE",
34
- "VIRTUAL_MACHINE_IMAGE", "VIRTUAL_NETWORK", "VOLUME", "WEB_SERVICE" ] }""",
34
+ "VIRTUAL_MACHINE_IMAGE", "VIRTUAL_NETWORK", "VOLUME", "WEB_SERVICE", "NETWORK_ADDRESS"] }""",
35
35
  ) # type: ignore
36
36
  wizAccessToken: RsVariableType(str, "", sensitive=True, required=False) # type: ignore
37
37
  wizClientId: RsVariableType(str, "", sensitive=True) # type: ignore
@@ -44,3 +44,4 @@ class WizVariables(metaclass=RsVariablesMeta):
44
44
  default=["SERVER_APPLICATION", "CLIENT_APPLICATION", "VIRTUAL_APPLIANCE"],
45
45
  required=False,
46
46
  ) # type: ignore
47
+ wizReportAge: RsVariableType(int, "14", default=14, required=False) # type: ignore
@@ -49,10 +49,10 @@ def wiz_authenticate(client_id: Optional[str] = None, client_secret: Optional[st
49
49
  # get secrets
50
50
  client_id = WizVariables.wizClientId if client_id is None else client_id
51
51
  if not client_id:
52
- raise ValueError("No Wiz Client ID provided in system environment or CLI command.")
52
+ error_and_exit("No Wiz Client ID provided in system environment or CLI command.")
53
53
  client_secret = WizVariables.wizClientSecret if client_secret is None else client_secret
54
54
  if not client_secret:
55
- raise ValueError("No Wiz Client Secret provided in system environment or CLI command.")
55
+ error_and_exit("No Wiz Client Secret provided in system environment or CLI command.")
56
56
  wiz_auth_url = config.get("wizAuthUrl")
57
57
  if not wiz_auth_url:
58
58
  error_and_exit("No Wiz Authentication URL provided in the init.yaml file.")
@@ -100,7 +100,7 @@ def get_token(api: Api, client_id: str, client_secret: str, token_url: str) -> t
100
100
  )
101
101
  if response.ok:
102
102
  status_code = 200
103
- logger.debug(response.reason)
103
+ logger.info(response.reason)
104
104
  # If response is unauthorized, try the first cognito url
105
105
  if response.status_code == requests.codes.unauthorized:
106
106
  try:
@@ -12,6 +12,7 @@ from ssp import SSP # type: ignore
12
12
 
13
13
  from regscale.core.app.api import Api
14
14
  from regscale.core.app.application import Application
15
+ from regscale.core.app.utils.app_utils import error_and_exit
15
16
  from regscale.integrations.public.fedramp.fedramp_common import (
16
17
  get_profile_info_by_id,
17
18
  logger,
@@ -38,10 +39,8 @@ from regscale.integrations.public.fedramp.fedramp_common import (
38
39
  post_ports,
39
40
  post_links,
40
41
  post_implementations,
41
- debug_logger,
42
42
  post_leveraged_authorizations,
43
43
  )
44
- from regscale.core.app.utils.app_utils import error_and_exit
45
44
  from regscale.models import ProfileMapping
46
45
 
47
46
 
@@ -365,7 +364,7 @@ def process_fedramp_docx(
365
364
  load_missing=load_missing,
366
365
  )
367
366
  except Exception as e:
368
- debug_logger.info(e, exc_info=True)
367
+ logger.info(e)
369
368
  logger.error(
370
369
  f"Unable to gather implementations: {e}",
371
370
  record_type="implementations",
@@ -247,6 +247,7 @@ class IntegrationAsset:
247
247
  mac_address: Optional[str] = None
248
248
  fqdn: Optional[str] = None
249
249
  ip_address: Optional[str] = None
250
+ ipv6_address: Optional[str] = None
250
251
  component_names: List[str] = dataclasses.field(default_factory=list)
251
252
  is_virtual: bool = True
252
253
 
@@ -1112,6 +1113,7 @@ class ScannerIntegration(ABC):
1112
1113
  azureIdentifier=asset.azure_identifier,
1113
1114
  location=asset.location,
1114
1115
  ipAddress=asset.ip_address,
1116
+ iPv6Address=asset.ipv6_address,
1115
1117
  fqdn=asset.fqdn,
1116
1118
  macAddress=asset.mac_address,
1117
1119
  diskStorage=asset.disk_storage,
@@ -1651,6 +1653,7 @@ class ScannerIntegration(ABC):
1651
1653
  issue.riskAdjustment = finding.risk_adjustment
1652
1654
  issue.operationalRequirement = finding.operational_requirements
1653
1655
  issue.deviationRationale = finding.deviation_rationale
1656
+ issue.dateLastUpdated = get_current_datetime()
1654
1657
 
1655
1658
  if finding.cve:
1656
1659
  issue = self.lookup_kev_and_upate_issue(cve=finding.cve, issue=issue, cisa_kevs=self._kev_data)
@@ -2323,7 +2326,7 @@ class ScannerIntegration(ABC):
2323
2326
  or finding.observations, # or finding.evidence, whichever is more appropriate
2324
2327
  port=finding.port if hasattr(finding, "port") else None,
2325
2328
  protocol=finding.protocol if hasattr(finding, "protocol") else None,
2326
- operatingSystem=asset.operating_system if hasattr(asset, "operating_system") else None,
2329
+ operatingSystem=asset.operatingSystem if hasattr(asset, "operatingSystem") else None,
2327
2330
  fixedVersions=finding.fixed_versions,
2328
2331
  buildVersion=finding.build_version,
2329
2332
  fixStatus=finding.fix_status,
@@ -2339,7 +2342,7 @@ class ScannerIntegration(ABC):
2339
2342
  vulnerabilityId=vulnerability.id,
2340
2343
  assetId=asset.id,
2341
2344
  scanId=scan_history.id,
2342
- securityPlansId=self.plan_id if not self.is_component else None,
2345
+ securityPlanId=self.plan_id if not self.is_component else None,
2343
2346
  createdById=self.assessor_id,
2344
2347
  tenantsId=self.tenant_id,
2345
2348
  isPublic=True,
@@ -2347,6 +2350,7 @@ class ScannerIntegration(ABC):
2347
2350
  firstSeen=finding.first_seen,
2348
2351
  lastSeen=finding.last_seen,
2349
2352
  status=finding.status,
2353
+ dateLastUpdated=get_current_datetime(),
2350
2354
  ).create_unique()
2351
2355
  return vulnerability
2352
2356
 
@@ -2534,6 +2538,7 @@ class ScannerIntegration(ABC):
2534
2538
  f"{get_current_datetime('%b %d, %Y')} - Closed by {self.title} for having no current vulnerabilities."
2535
2539
  )
2536
2540
  issue.changes = f"{issue.changes}\n{changes_text}" if issue.changes else changes_text
2541
+ issue.dateLastUpdated = get_current_datetime()
2537
2542
  issue.save()
2538
2543
 
2539
2544
  with count_lock:
@@ -1,9 +1,54 @@
1
1
  {
2
2
  "title": "CISA Catalog of Known Exploited Vulnerabilities",
3
- "catalogVersion": "2025.07.10",
4
- "dateReleased": "2025-07-10T16:05:09.522Z",
5
- "count": 1379,
3
+ "catalogVersion": "2025.07.20",
4
+ "dateReleased": "2025-07-20T19:06:00.8332Z",
5
+ "count": 1382,
6
6
  "vulnerabilities": [
7
+ {
8
+ "cveID": "CVE-2025-53770",
9
+ "vendorProject": "Microsoft",
10
+ "product": "SharePoint",
11
+ "vulnerabilityName": "Microsoft SharePoint Deserialization of Untrusted Data Vulnerability",
12
+ "dateAdded": "2025-07-20",
13
+ "shortDescription": "Microsoft SharePoint Server on-premises contains a deserialization of untrusted data vulnerability that could allow an unauthorized attacker to execute code over a network.",
14
+ "requiredAction": "CISA recommends configuring AMSI integration in SharePoint and deploying Defender AV on all SharePoint servers. If AMSI cannot be enabled, CISA recommends disconnecting affected products that are public-facing on the internet from service until official mitigations are available. Once mitigations are provided, apply them according to CISA and vendor instructions. Follow the applicable BOD 22-01 guidance for cloud services or discontinue use of the product if mitigations are not available. ",
15
+ "dueDate": "2025-07-21",
16
+ "knownRansomwareCampaignUse": "Unknown",
17
+ "notes": "CISA Mitigation Instructions: https:\/\/www.cisa.gov\/news-events\/alerts\/2025\/07\/20\/microsoft-releases-guidance-exploitation-sharepoint-vulnerability-cve-2025-53770 ; https:\/\/msrc.microsoft.com\/blog\/2025\/07\/customer-guidance-for-sharepoint-vulnerability-cve-2025-53770\/ ; https:\/\/msrc.microsoft.com\/update-guide\/vulnerability\/CVE-2025-53770 ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2025-53770",
18
+ "cwes": [
19
+ "CWE-502"
20
+ ]
21
+ },
22
+ {
23
+ "cveID": "CVE-2025-25257",
24
+ "vendorProject": "Fortinet",
25
+ "product": "FortiWeb",
26
+ "vulnerabilityName": "Fortinet FortiWeb SQL Injection Vulnerability",
27
+ "dateAdded": "2025-07-18",
28
+ "shortDescription": "Fortinet FortiWeb contains a SQL injection vulnerability that may allow an unauthenticated attacker to execute unauthorized SQL code or commands via crafted HTTP or HTTPs requests.",
29
+ "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
30
+ "dueDate": "2025-08-08",
31
+ "knownRansomwareCampaignUse": "Unknown",
32
+ "notes": "https:\/\/fortiguard.fortinet.com\/psirt\/FG-IR-25-151 ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2025-25257",
33
+ "cwes": [
34
+ "CWE-89"
35
+ ]
36
+ },
37
+ {
38
+ "cveID": "CVE-2025-47812",
39
+ "vendorProject": "Wing FTP Server",
40
+ "product": "Wing FTP Server",
41
+ "vulnerabilityName": "Wing FTP Server Improper Neutralization of Null Byte or NUL Character Vulnerability",
42
+ "dateAdded": "2025-07-14",
43
+ "shortDescription": "Wing FTP Server contains an improper neutralization of null byte or NUL character vulnerability that can allow injection of arbitrary Lua code into user session files. This can be used to execute arbitrary system commands with the privileges of the FTP service (root or SYSTEM by default).",
44
+ "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
45
+ "dueDate": "2025-08-04",
46
+ "knownRansomwareCampaignUse": "Unknown",
47
+ "notes": "https:\/\/www.wftpserver.com\/serverhistory.htm ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2025-47812",
48
+ "cwes": [
49
+ "CWE-158"
50
+ ]
51
+ },
7
52
  {
8
53
  "cveID": "CVE-2025-5777",
9
54
  "vendorProject": "Citrix",
@@ -150,7 +195,7 @@
150
195
  "shortDescription": "Fortinet FortiOS contains a use of hard-coded credentials vulnerability that could allow an attacker to cipher sensitive data in FortiOS configuration backup file via knowledge of the hard-coded key. ",
151
196
  "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
152
197
  "dueDate": "2025-07-16",
153
- "knownRansomwareCampaignUse": "Unknown",
198
+ "knownRansomwareCampaignUse": "Known",
154
199
  "notes": "https:\/\/fortiguard.com\/advisory\/FG-IR-19-007 ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2019-6693",
155
200
  "cwes": [
156
201
  "CWE-798"
@@ -18491,7 +18536,7 @@
18491
18536
  "shortDescription": "Microsoft Remote Desktop Services, formerly known as Terminal Service, contains an unspecified vulnerability that allows an unauthenticated attacker to connect to the target system using RDP and send specially crafted requests. Successful exploitation allows for remote code execution. The vulnerability is also known under the moniker of BlueKeep.",
18492
18537
  "requiredAction": "Apply updates per vendor instructions.",
18493
18538
  "dueDate": "2022-05-03",
18494
- "knownRansomwareCampaignUse": "Unknown",
18539
+ "knownRansomwareCampaignUse": "Known",
18495
18540
  "notes": "https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2019-0708",
18496
18541
  "cwes": [
18497
18542
  "CWE-416"