regscale-cli 6.25.1.0__py3-none-any.whl → 6.26.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 (80) hide show
  1. regscale/_version.py +1 -1
  2. regscale/airflow/hierarchy.py +2 -2
  3. regscale/core/app/application.py +18 -3
  4. regscale/core/app/internal/login.py +0 -1
  5. regscale/core/app/utils/catalog_utils/common.py +1 -1
  6. regscale/integrations/commercial/sicura/api.py +14 -13
  7. regscale/integrations/commercial/sicura/commands.py +8 -2
  8. regscale/integrations/commercial/sicura/scanner.py +49 -39
  9. regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
  10. regscale/integrations/commercial/wizv2/click.py +26 -26
  11. regscale/integrations/commercial/wizv2/compliance_report.py +152 -157
  12. regscale/integrations/commercial/wizv2/scanner.py +3 -3
  13. regscale/integrations/compliance_integration.py +67 -2
  14. regscale/integrations/control_matcher.py +358 -0
  15. regscale/integrations/milestone_manager.py +291 -0
  16. regscale/integrations/public/__init__.py +1 -0
  17. regscale/integrations/public/cci_importer.py +37 -38
  18. regscale/integrations/public/fedramp/click.py +60 -2
  19. regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
  20. regscale/integrations/scanner_integration.py +150 -96
  21. regscale/models/integration_models/cisa_kev_data.json +154 -4
  22. regscale/models/integration_models/nexpose.py +36 -10
  23. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  24. regscale/models/locking.py +12 -8
  25. regscale/models/platform.py +1 -2
  26. regscale/models/regscale_models/control_implementation.py +46 -21
  27. regscale/models/regscale_models/issue.py +256 -94
  28. regscale/models/regscale_models/milestone.py +1 -1
  29. regscale/models/regscale_models/regscale_model.py +6 -1
  30. regscale/templates/__init__.py +0 -0
  31. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.26.0.0.dist-info}/METADATA +1 -1
  32. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.26.0.0.dist-info}/RECORD +80 -33
  33. tests/regscale/integrations/commercial/__init__.py +0 -0
  34. tests/regscale/integrations/commercial/conftest.py +28 -0
  35. tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
  36. tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
  37. tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
  38. tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
  39. tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
  40. tests/regscale/integrations/commercial/test_aws.py +3731 -0
  41. tests/regscale/integrations/commercial/test_burp.py +48 -0
  42. tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
  43. tests/regscale/integrations/commercial/test_dependabot.py +341 -0
  44. tests/regscale/integrations/commercial/test_gcp.py +1543 -0
  45. tests/regscale/integrations/commercial/test_gitlab.py +549 -0
  46. tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
  47. tests/regscale/integrations/commercial/test_jira.py +1814 -0
  48. tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
  49. tests/regscale/integrations/commercial/test_okta.py +1228 -0
  50. tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
  51. tests/regscale/integrations/commercial/test_sicura.py +350 -0
  52. tests/regscale/integrations/commercial/test_snow.py +423 -0
  53. tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
  54. tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
  55. tests/regscale/integrations/commercial/test_stig.py +33 -0
  56. tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
  57. tests/regscale/integrations/commercial/test_stigv2.py +406 -0
  58. tests/regscale/integrations/commercial/test_wiz.py +1469 -0
  59. tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
  60. tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
  61. tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
  62. tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
  63. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
  64. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1351 -0
  65. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
  66. tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
  67. tests/regscale/integrations/commercial/wizv2/test_wiz_policy_compliance.py +750 -0
  68. tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
  69. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +264 -0
  70. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +624 -0
  71. tests/regscale/integrations/public/fedramp/__init__.py +1 -0
  72. tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
  73. tests/regscale/integrations/test_control_matcher.py +1314 -0
  74. tests/regscale/integrations/test_control_matching.py +155 -0
  75. tests/regscale/integrations/test_milestone_manager.py +408 -0
  76. tests/regscale/models/test_issue.py +378 -1
  77. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.26.0.0.dist-info}/LICENSE +0 -0
  78. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.26.0.0.dist-info}/WHEEL +0 -0
  79. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.26.0.0.dist-info}/entry_points.txt +0 -0
  80. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.26.0.0.dist-info}/top_level.txt +0 -0
regscale/_version.py CHANGED
@@ -33,7 +33,7 @@ def get_version_from_pyproject() -> str:
33
33
  return match.group(1)
34
34
  except Exception:
35
35
  pass
36
- return "6.25.1.0" # fallback version
36
+ return "6.26.0.0" # fallback version
37
37
 
38
38
 
39
39
  __version__ = get_version_from_pyproject()
@@ -2,9 +2,9 @@
2
2
 
3
3
  from typing import Optional
4
4
 
5
- from regscale.regscale import cli
6
- from regscale.models.click_models import ClickCommand
7
5
  from regscale.airflow.click_mixins import AirflowClickGroup
6
+ from regscale.models.click_models import ClickCommand
7
+ from regscale.regscale import cli
8
8
 
9
9
  AIRFLOW_CLICK_GROUP = AirflowClickGroup.from_group(cli, prefix="regscale")
10
10
  AIRFLOW_CLICK_OPERATORS = AIRFLOW_CLICK_GROUP.flatten_operator()
@@ -664,7 +664,8 @@ class Application(metaclass=Singleton):
664
664
 
665
665
  def save_config(self, conf: dict) -> None:
666
666
  """
667
- Save Configuration to init.yaml
667
+ Save Configuration to init.yaml using atomic file operations to prevent corruption
668
+ during parallel writes.
668
669
 
669
670
  :param dict conf: Application configuration
670
671
  :rtype: None
@@ -681,8 +682,22 @@ class Application(metaclass=Singleton):
681
682
  try:
682
683
  self.logger.debug(f"Saving config to {self.config_file}.")
683
684
  with self._config_lock:
684
- with open(self.config_file, "w", encoding="utf-8") as file:
685
- yaml.dump(conf, file)
685
+ # Use atomic file operations: write to temp file, then rename
686
+ # This prevents corruption when multiple processes write simultaneously
687
+ import tempfile
688
+
689
+ config_dir = os.path.dirname(self.config_file) or "."
690
+ temp_fd, temp_path = tempfile.mkstemp(dir=config_dir, prefix=".tmp_", suffix=".yaml", text=True)
691
+ try:
692
+ with os.fdopen(temp_fd, "w", encoding="utf-8") as temp_file:
693
+ yaml.dump(conf, temp_file)
694
+ # Atomic rename - this is atomic on POSIX systems
695
+ os.replace(temp_path, self.config_file)
696
+ except Exception:
697
+ # Clean up temp file if something goes wrong
698
+ with contextlib.suppress(OSError):
699
+ os.unlink(temp_path)
700
+ raise
686
701
  except OSError:
687
702
  self.logger.error(f"Could not save config to {self.config_file}.")
688
703
 
@@ -136,7 +136,6 @@ def login(
136
136
  logger.info("New RegScale Token has been updated and saved in init.yaml")
137
137
  # Truncate token for logging purposes
138
138
  logger.debug("Token: %s", regscale_auth.token[:20])
139
- config["domain"] = host
140
139
  app.save_config(config)
141
140
  return regscale_auth.token
142
141
 
@@ -87,5 +87,5 @@ def objective_to_control_dot(input_string: str) -> str:
87
87
  if match:
88
88
  return match.group(1)
89
89
  else:
90
- logger.error(f"Failed to convert objective to control: {input_string}")
90
+ logger.debug(f"Failed to convert objective to control: {input_string}")
91
91
  return input_string
@@ -356,7 +356,7 @@ class SicuraAPI:
356
356
  try:
357
357
  response = self._make_request(
358
358
  "GET",
359
- "/backend/api/jaeger/v1/nodes",
359
+ "/api/jaeger/v1/nodes",
360
360
  params={
361
361
  "verbose": "true",
362
362
  "attributes": "platforms,scannable_profiles,most_recent_scan",
@@ -425,7 +425,7 @@ class SicuraAPI:
425
425
  "scanAttributes": {"platform": platform, "profile": profile},
426
426
  }
427
427
 
428
- result = self._make_request("POST", "/backend/api/jaeger/v1/tasks/", data=payload)
428
+ result = self._make_request("POST", "/api/jaeger/v1/tasks/", data=payload)
429
429
 
430
430
  if result:
431
431
  logger.info(f"Successfully created scan task with ID: {result}")
@@ -448,7 +448,7 @@ class SicuraAPI:
448
448
  """
449
449
  try:
450
450
  response = self._make_request(
451
- "GET", "/backend/api/jaeger/v1/jobs", params={"verbose": "true", self.FILTER_TASK_ID: task_id}
451
+ "GET", "/api/jaeger/v1/jobs", params={"verbose": "true", self.FILTER_TASK_ID: task_id}
452
452
  )
453
453
 
454
454
  # Handle 404 or empty response
@@ -503,7 +503,7 @@ class SicuraAPI:
503
503
  if profile:
504
504
  params["profile"] = profile
505
505
 
506
- response = self._make_request("GET", "/backend/api/jaeger/v1/nodes", params=params)
506
+ response = self._make_request("GET", "/api/jaeger/v1/nodes", params=params)
507
507
 
508
508
  # Handle 404 or empty response
509
509
  if not response or (isinstance(response, list) and not response):
@@ -524,16 +524,17 @@ class SicuraAPI:
524
524
  return None # Return None if no scans available
525
525
 
526
526
  # Calculate summary stats
527
- pass_count = sum(1 for scan in device.get("scans", []) if scan.get("result") == "pass")
528
- fail_count = sum(1 for scan in device.get("scans", []) if scan.get("result") == "fail")
529
- total_count = len(device.get("scans", []))
527
+ scan_results = device.get("scans", {}).get("results", [])
528
+ pass_count = sum(1 for scan in scan_results if scan.get("result") == "pass")
529
+ fail_count = sum(1 for scan in scan_results if scan.get("result") == "fail")
530
+ total_count = len(scan_results)
530
531
 
531
532
  # Create the raw result data
532
533
  result_data = {
533
534
  "device_id": device.get("id"),
534
535
  "fqdn": device.get("fqdn"),
535
536
  "ip_address": device.get("ip_address"),
536
- "scans": device.get("scans", []),
537
+ "scans": scan_results,
537
538
  "summary": {
538
539
  "total": total_count,
539
540
  "pass": pass_count,
@@ -638,7 +639,7 @@ class SicuraAPI:
638
639
  if ip_address:
639
640
  params[self.FILTER_IP_ADDRESS] = ip_address
640
641
 
641
- response = self._make_request("GET", "/backend/api/jaeger/v1/node_templates/", params=params)
642
+ response = self._make_request("GET", "/api/jaeger/v1/node_templates/", params=params)
642
643
 
643
644
  # Handle 404 or empty response
644
645
  if not response or (isinstance(response, dict) and "detail" in response):
@@ -670,7 +671,7 @@ class SicuraAPI:
670
671
  # Use PUT method with the correct endpoint and payload
671
672
  result = self._make_request(
672
673
  "PUT",
673
- f"/backend/api/jaeger/v1/node_templates/{device_id}",
674
+ f"/api/jaeger/v1/node_templates/{device_id}",
674
675
  params={"verbose": "true", "include_controls": "true", "action": "promote"},
675
676
  )
676
677
 
@@ -697,7 +698,7 @@ class SicuraAPI:
697
698
  # Use PUT method with the correct endpoint and payload
698
699
  result = self._make_request(
699
700
  "PUT",
700
- f"/backend/api/jaeger/v1/node_templates/{device_id}",
701
+ f"/api/jaeger/v1/node_templates/{device_id}",
701
702
  params={"verbose": "true", "include_controls": "true", "action": "reject"},
702
703
  )
703
704
 
@@ -721,7 +722,7 @@ class SicuraAPI:
721
722
  :rtype: bool
722
723
  """
723
724
  try:
724
- result = self._make_request("DELETE", f"/backend/api/jaeger/v1/nodes/{device_id}")
725
+ result = self._make_request("DELETE", f"/api/jaeger/v1/nodes/{device_id}")
725
726
 
726
727
  # For DELETE operations, an empty result typically indicates success
727
728
  if result is None or result == "" or (isinstance(result, dict) and not result):
@@ -854,7 +855,7 @@ class SicuraAPI:
854
855
  }
855
856
 
856
857
  logger.info(f"Creating enforcement task for device {device_id} with {len(ce_names)} CE names")
857
- result = self._make_request("POST", "/backend/api/jaeger/v1/tasks/", data=payload)
858
+ result = self._make_request("POST", "/api/jaeger/v1/tasks/", data=payload)
858
859
 
859
860
  if result:
860
861
  logger.info(f"Successfully created enforcement task with ID: {result}")
@@ -46,7 +46,13 @@ def sync_assets(regscale_id: int):
46
46
 
47
47
  @sicura.command(name="sync_findings")
48
48
  @regscale_id(help="RegScale will create and update findings as children of this record.")
49
- def sync_findings(regscale_id: int):
49
+ @click.option(
50
+ "--trigger_scan",
51
+ "-s",
52
+ is_flag=True,
53
+ help="Trigger a new scan on Sicura assets before syncing.",
54
+ )
55
+ def sync_findings(regscale_id: int, trigger_scan: bool):
50
56
  """
51
57
  Sync Sicura findings to RegScale.
52
58
 
@@ -60,7 +66,7 @@ def sync_findings(regscale_id: int):
60
66
  )
61
67
 
62
68
  # Using import_findings method which handles the synchronization
63
- integration.sync_findings(plan_id=regscale_id)
69
+ integration.sync_findings(plan_id=regscale_id, trigger_scan=trigger_scan)
64
70
 
65
71
  logger.info("[bold green]Finding synchronization complete.")
66
72
 
@@ -74,6 +74,10 @@ class SicuraIntegration(ScannerIntegration):
74
74
  logger.warning("No devices found in Sicura")
75
75
  return
76
76
 
77
+ if kwargs.pop("trigger_scan", False):
78
+ logger.info(f"Triggering scans on Sicura {len(devices)} devices...")
79
+ self.trigger_scans(devices)
80
+
77
81
  self.num_findings_to_process = 0
78
82
  findings_count = 0
79
83
 
@@ -206,7 +210,8 @@ class SicuraIntegration(ScannerIntegration):
206
210
  mitigation=mitigation,
207
211
  )
208
212
 
209
- def _extract_scan_data(self, scan: Any) -> dict:
213
+ @staticmethod
214
+ def _extract_scan_data(scan: Any) -> dict:
210
215
  """
211
216
  Extract scan data from the scan object
212
217
 
@@ -235,7 +240,8 @@ class SicuraIntegration(ScannerIntegration):
235
240
  "controls": scan.controls if hasattr(scan, "controls") else {},
236
241
  }
237
242
 
238
- def _extract_control_refs(self, controls: dict) -> tuple:
243
+ @staticmethod
244
+ def _extract_control_refs(controls: dict) -> tuple:
239
245
  """
240
246
  Extract CCI and SRG references from controls
241
247
 
@@ -353,8 +359,9 @@ class SicuraIntegration(ScannerIntegration):
353
359
  baseline=platform,
354
360
  )
355
361
 
362
+ @staticmethod
356
363
  def _create_results_text(
357
- self, title, ce_name, result, description, state, state_reason, cci_ref, srg_refs, is_cci_finding
364
+ title, ce_name, result, description, state, state_reason, cci_ref, srg_refs, is_cci_finding
358
365
  ) -> str:
359
366
  """
360
367
  Create the results text for a finding
@@ -401,42 +408,6 @@ class SicuraIntegration(ScannerIntegration):
401
408
  :rtype: Iterator[IntegrationAsset]
402
409
  """
403
410
  logger.info("Fetching assets from Sicura...")
404
-
405
- # Get all devices
406
- pending_devices = self.api.get_pending_devices()
407
- for pending_device in pending_devices:
408
- print(f"Pending device: {pending_device}")
409
- self.api.accept_pending_device(pending_device.id)
410
- task_id = self.api.create_scan_task(
411
- device_id=pending_device.id,
412
- platform=pending_device.platform,
413
- profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
414
- )
415
- if task_id:
416
- self.api.wait_for_scan_results(
417
- task_id=task_id,
418
- fqdn=pending_device.fqdn,
419
- platform=pending_device.platform,
420
- profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
421
- )
422
- else:
423
- logger.warning(f"Failed to create scan task for device {pending_device.fqdn}")
424
- continue # Continue to next device if creating scan task failed
425
-
426
- task_id = self.api.create_scan_task(
427
- device_id=pending_device.id, platform=pending_device.platform, profile=SicuraProfile.LEVEL_1_SERVER
428
- )
429
- if task_id:
430
- self.api.wait_for_scan_results(
431
- task_id=task_id,
432
- fqdn=pending_device.fqdn,
433
- platform=pending_device.platform,
434
- profile=SicuraProfile.LEVEL_1_SERVER,
435
- )
436
- else:
437
- logger.warning(f"Failed to create scan task for device {pending_device.fqdn}")
438
- # No continue needed here as we're at the end of the loop iteration
439
-
440
411
  devices = self.api.get_devices()
441
412
 
442
413
  if not devices:
@@ -479,3 +450,42 @@ class SicuraIntegration(ScannerIntegration):
479
450
  )
480
451
 
481
452
  self.asset_progress.update(loading_devices, advance=1)
453
+
454
+ def trigger_and_wait_for_scan(self, device: Device) -> None:
455
+ """
456
+ Trigger a scan and wait for the results
457
+
458
+ :param Device device: The device to trigger a scan for
459
+ :return: None
460
+ """
461
+ task_id = self.api.create_scan_task(
462
+ device_id=device.id,
463
+ platform=device.platforms,
464
+ profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
465
+ )
466
+ if task_id:
467
+ self.api.wait_for_scan_results(
468
+ task_id=task_id,
469
+ fqdn=device.fqdn,
470
+ platform=device.platforms,
471
+ profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
472
+ )
473
+ else:
474
+ logger.warning(f"Failed to create scan task for device {device.fqdn}")
475
+
476
+ def trigger_scans(self, devices: list[Device]) -> None:
477
+ """
478
+ Trigger scans for a list of devices
479
+
480
+ :param list[Device] devices: The devices to trigger scans for
481
+ :return: None
482
+ """
483
+ if len(devices) > 1:
484
+ from regscale.utils.threading import ThreadManager
485
+
486
+ # use multithreading to trigger scans for multiple devices
487
+ thread_manager = ThreadManager(max_workers=10)
488
+ thread_manager.submit_tasks_from_list(self.trigger_and_wait_for_scan, devices)
489
+ thread_manager.execute_and_verify()
490
+ else:
491
+ self.trigger_and_wait_for_scan(devices[0])
@@ -82,15 +82,15 @@ class STIGInfo(BaseModel):
82
82
  """Data model for STIG Information with optional and required attributes."""
83
83
 
84
84
  version: str
85
- classification: str
85
+ classification: Optional[str] = None
86
86
  customname: Optional[str] = None
87
87
  stigid: str
88
88
  description: Optional[str] = None
89
89
  filename: Optional[str] = None
90
90
  releaseinfo: str
91
91
  title: str
92
- uuid: str
93
- notice: str
92
+ uuid: Optional[str] = None
93
+ notice: Optional[str] = None
94
94
  source: Optional[str] = None
95
95
 
96
96
 
@@ -115,10 +115,10 @@ class Vuln(BaseModel):
115
115
  check_content: Optional[str] = None
116
116
  fix_text: str
117
117
  check_content_ref: Optional[str] = None
118
- weight: str
118
+ weight: Optional[str] = None
119
119
  stigref: Optional[str] = None
120
120
  targetkey: Optional[str] = None
121
- stig_uuid: str
121
+ stig_uuid: Optional[str] = None
122
122
  vuln_discuss: Optional[str] = None
123
123
  ia_controls: Optional[str] = None
124
124
  class_: Optional[str] = None
@@ -45,7 +45,7 @@ def authenticate(client_id, client_secret):
45
45
  "--client_id",
46
46
  "-ci",
47
47
  help="Wiz Client ID, or can be set as environment variable wizClientId",
48
- default="",
48
+ default=None,
49
49
  hide_input=False,
50
50
  required=False,
51
51
  )
@@ -53,7 +53,7 @@ def authenticate(client_id, client_secret):
53
53
  "--client_secret",
54
54
  "-cs",
55
55
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
56
- default="",
56
+ default=None,
57
57
  hide_input=False,
58
58
  required=False,
59
59
  )
@@ -77,9 +77,9 @@ def inventory(
77
77
  """Process inventory from Wiz and create assets in RegScale."""
78
78
  from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
79
79
 
80
- if not client_secret:
80
+ if client_secret is None:
81
81
  client_secret = WizVariables.wizClientSecret
82
- if not client_id:
82
+ if client_id is None:
83
83
  client_id = WizVariables.wizClientId
84
84
 
85
85
  scanner = WizVulnerabilityIntegration(plan_id=regscale_ssp_id)
@@ -104,14 +104,14 @@ def inventory(
104
104
  @click.option(
105
105
  "--client_id",
106
106
  help="Wiz Client ID, or can be set as environment variable wizClientId",
107
- default="",
107
+ default=None,
108
108
  hide_input=False,
109
109
  required=False,
110
110
  )
111
111
  @click.option(
112
112
  "--client_secret",
113
113
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
114
- default="",
114
+ default=None,
115
115
  hide_input=True,
116
116
  required=False,
117
117
  )
@@ -140,9 +140,9 @@ def issues(
140
140
  from regscale.integrations.commercial.wizv2.issue import WizIssue
141
141
  import json
142
142
 
143
- if not client_secret:
143
+ if client_secret is None:
144
144
  client_secret = WizVariables.wizClientSecret
145
- if not client_id:
145
+ if client_id is None:
146
146
  client_id = WizVariables.wizClientId
147
147
 
148
148
  check_license()
@@ -166,7 +166,7 @@ def issues(
166
166
  "--client_id",
167
167
  "-ci",
168
168
  help="Wiz Client ID, or can be set as environment variable wizClientId",
169
- default="",
169
+ default=None,
170
170
  hide_input=False,
171
171
  required=False,
172
172
  )
@@ -174,7 +174,7 @@ def issues(
174
174
  "--client_secret",
175
175
  "-cs",
176
176
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
177
- default="",
177
+ default=None,
178
178
  hide_input=True,
179
179
  required=False,
180
180
  )
@@ -194,9 +194,9 @@ def attach_sbom(
194
194
  from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
195
195
  from regscale.integrations.commercial.wizv2.utils import fetch_sbom_report
196
196
 
197
- if not client_secret:
197
+ if client_secret is None:
198
198
  client_secret = WizVariables.wizClientSecret
199
- if not client_id:
199
+ if client_id is None:
200
200
  client_id = WizVariables.wizClientId
201
201
 
202
202
  wiz_authenticate(
@@ -234,7 +234,7 @@ def threats():
234
234
  "--client_id",
235
235
  "-ci",
236
236
  help="Wiz Client ID, or can be set as environment variable wizClientId",
237
- default="",
237
+ default=None,
238
238
  hide_input=False,
239
239
  required=False,
240
240
  )
@@ -242,7 +242,7 @@ def threats():
242
242
  "--client_secret",
243
243
  "-cs",
244
244
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
245
- default="",
245
+ default=None,
246
246
  hide_input=True,
247
247
  required=False,
248
248
  )
@@ -266,9 +266,9 @@ def vulnerabilities(
266
266
  """Process vulnerabilities from Wiz"""
267
267
  from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
268
268
 
269
- if not client_secret:
269
+ if client_secret is None:
270
270
  client_secret = WizVariables.wizClientSecret
271
- if not client_id:
271
+ if client_id is None:
272
272
  client_id = WizVariables.wizClientId
273
273
 
274
274
  scanner = WizVulnerabilityIntegration(plan_id=regscale_ssp_id)
@@ -286,7 +286,7 @@ def vulnerabilities(
286
286
  "--client_id",
287
287
  "-ci",
288
288
  help="Wiz Client ID, or can be set as environment variable wizClientId",
289
- default="",
289
+ default=None,
290
290
  hide_input=False,
291
291
  required=False,
292
292
  )
@@ -294,7 +294,7 @@ def vulnerabilities(
294
294
  "--client_secret",
295
295
  "-cs",
296
296
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
297
- default="",
297
+ default=None,
298
298
  hide_input=True,
299
299
  required=False,
300
300
  )
@@ -314,9 +314,9 @@ def add_report_evidence(
314
314
  from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
315
315
  from regscale.integrations.commercial.wizv2.utils import fetch_report_by_id
316
316
 
317
- if not client_secret:
317
+ if client_secret is None:
318
318
  client_secret = WizVariables.wizClientSecret
319
- if not client_id:
319
+ if client_id is None:
320
320
  client_id = WizVariables.wizClientId
321
321
 
322
322
  wiz_authenticate(
@@ -346,7 +346,7 @@ def add_report_evidence(
346
346
  "--client_id",
347
347
  "-i",
348
348
  help="Wiz Client ID, or can be set as environment variable wizClientId",
349
- default="",
349
+ default=None,
350
350
  hide_input=False,
351
351
  required=False,
352
352
  )
@@ -354,7 +354,7 @@ def add_report_evidence(
354
354
  "--client_secret",
355
355
  "-s",
356
356
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
357
- default="",
357
+ default=None,
358
358
  hide_input=True,
359
359
  required=False,
360
360
  )
@@ -444,9 +444,9 @@ def sync_compliance(
444
444
  return
445
445
 
446
446
  # Use environment variables if not provided
447
- if not client_secret:
447
+ if client_secret is None:
448
448
  client_secret = WizVariables.wizClientSecret
449
- if not client_id:
449
+ if client_id is None:
450
450
  client_id = WizVariables.wizClientId
451
451
 
452
452
  # Resolve framework ID using the enhanced framework resolution
@@ -582,9 +582,9 @@ def compliance_report(
582
582
  from regscale.integrations.commercial.wizv2.compliance_report import WizComplianceReportProcessor
583
583
 
584
584
  # Use environment variables if not provided
585
- if not client_secret:
585
+ if client_secret is None:
586
586
  client_secret = WizVariables.wizClientSecret
587
- if not client_id:
587
+ if client_id is None:
588
588
  client_id = WizVariables.wizClientId
589
589
 
590
590
  # Create and run the compliance report processor