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

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

Potentially problematic release.


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

Files changed (146) hide show
  1. regscale/_version.py +1 -1
  2. regscale/airflow/hierarchy.py +2 -2
  3. regscale/core/app/application.py +19 -4
  4. regscale/core/app/internal/evidence.py +419 -2
  5. regscale/core/app/internal/login.py +0 -1
  6. regscale/core/app/utils/catalog_utils/common.py +1 -1
  7. regscale/dev/code_gen.py +24 -20
  8. regscale/integrations/commercial/jira.py +367 -126
  9. regscale/integrations/commercial/qualys/__init__.py +7 -8
  10. regscale/integrations/commercial/qualys/scanner.py +8 -3
  11. regscale/integrations/commercial/sicura/api.py +14 -13
  12. regscale/integrations/commercial/sicura/commands.py +8 -2
  13. regscale/integrations/commercial/sicura/scanner.py +49 -39
  14. regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
  15. regscale/integrations/commercial/synqly/assets.py +17 -0
  16. regscale/integrations/commercial/synqly/vulnerabilities.py +45 -28
  17. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  18. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  19. regscale/integrations/commercial/tenablev2/commands.py +142 -1
  20. regscale/integrations/commercial/tenablev2/scanner.py +0 -1
  21. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  22. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  23. regscale/integrations/commercial/wizv2/click.py +64 -79
  24. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  25. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  26. regscale/integrations/commercial/wizv2/compliance_report.py +161 -165
  27. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  28. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +3 -3
  29. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +1 -17
  30. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  31. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  32. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +5 -9
  33. regscale/integrations/commercial/wizv2/issue.py +1 -1
  34. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  35. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  36. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  37. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  38. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  39. regscale/integrations/commercial/wizv2/reports.py +1 -1
  40. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  41. regscale/integrations/commercial/wizv2/scanner.py +39 -99
  42. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  43. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  44. regscale/integrations/commercial/wizv2/variables.py +89 -3
  45. regscale/integrations/compliance_integration.py +60 -41
  46. regscale/integrations/control_matcher.py +377 -0
  47. regscale/integrations/due_date_handler.py +14 -8
  48. regscale/integrations/milestone_manager.py +291 -0
  49. regscale/integrations/public/__init__.py +1 -0
  50. regscale/integrations/public/cci_importer.py +37 -38
  51. regscale/integrations/public/fedramp/click.py +60 -2
  52. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  53. regscale/integrations/public/fedramp/fedramp_cis_crm.py +393 -340
  54. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  55. regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
  56. regscale/integrations/scanner_integration.py +277 -153
  57. regscale/models/integration_models/cisa_kev_data.json +282 -9
  58. regscale/models/integration_models/nexpose.py +36 -10
  59. regscale/models/integration_models/qualys.py +3 -4
  60. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  61. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +24 -7
  62. regscale/models/integration_models/synqly_models/synqly_model.py +8 -1
  63. regscale/models/locking.py +12 -8
  64. regscale/models/platform.py +1 -2
  65. regscale/models/regscale_models/control_implementation.py +47 -22
  66. regscale/models/regscale_models/issue.py +256 -95
  67. regscale/models/regscale_models/milestone.py +1 -1
  68. regscale/models/regscale_models/regscale_model.py +6 -1
  69. regscale/templates/__init__.py +0 -0
  70. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/METADATA +1 -17
  71. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/RECORD +145 -65
  72. tests/regscale/integrations/commercial/__init__.py +0 -0
  73. tests/regscale/integrations/commercial/conftest.py +28 -0
  74. tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
  75. tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
  76. tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
  77. tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
  78. tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
  79. tests/regscale/integrations/commercial/test_aws.py +3731 -0
  80. tests/regscale/integrations/commercial/test_burp.py +48 -0
  81. tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
  82. tests/regscale/integrations/commercial/test_dependabot.py +341 -0
  83. tests/regscale/integrations/commercial/test_gcp.py +1543 -0
  84. tests/regscale/integrations/commercial/test_gitlab.py +549 -0
  85. tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
  86. tests/regscale/integrations/commercial/test_jira.py +2204 -0
  87. tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
  88. tests/regscale/integrations/commercial/test_okta.py +1228 -0
  89. tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
  90. tests/regscale/integrations/commercial/test_sicura.py +350 -0
  91. tests/regscale/integrations/commercial/test_snow.py +423 -0
  92. tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
  93. tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
  94. tests/regscale/integrations/commercial/test_stig.py +33 -0
  95. tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
  96. tests/regscale/integrations/commercial/test_stigv2.py +406 -0
  97. tests/regscale/integrations/commercial/test_wiz.py +1365 -0
  98. tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
  99. tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
  100. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  101. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  102. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  103. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  104. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  105. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  106. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  107. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  108. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  109. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  110. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  111. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  112. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  113. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  114. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  115. tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
  116. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  117. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  118. tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
  119. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  120. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  121. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  122. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  123. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
  124. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
  125. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
  126. tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
  127. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  128. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  129. tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
  130. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1132 -0
  131. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
  132. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  133. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  134. tests/regscale/integrations/public/fedramp/__init__.py +1 -0
  135. tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
  136. tests/regscale/integrations/public/test_fedramp.py +301 -0
  137. tests/regscale/integrations/test_control_matcher.py +1397 -0
  138. tests/regscale/integrations/test_control_matching.py +155 -0
  139. tests/regscale/integrations/test_milestone_manager.py +408 -0
  140. tests/regscale/models/test_issue.py +378 -1
  141. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3543
  142. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  143. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/LICENSE +0 -0
  144. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/WHEEL +0 -0
  145. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/entry_points.txt +0 -0
  146. {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,89 @@ from regscale.core.app.utils.app_utils import get_current_datetime
8
8
  from regscale.integrations.scanner_integration import IntegrationFinding, issue_due_date
9
9
  from regscale.models import regscale_models
10
10
 
11
- logger = logging.getLogger(__name__)
11
+ logger = logging.getLogger("regscale")
12
+
13
+ # Constants
14
+ DEFAULT_CCI_REF = "CCI-000366" # Default CCI reference for STIG findings
15
+ UNKNOWN_VULN_NUM = "unknown"
16
+ _SOLUTION_KEYWORD = "Solution:"
17
+ _REFERENCE_INFO_KEYWORD = "Reference Information:"
18
+
19
+ # Status mapping for STIG results
20
+ _RESULT_STATUS_MAP = {
21
+ "PASSED": regscale_models.ChecklistStatus.PASS,
22
+ "FAILED": regscale_models.ChecklistStatus.FAIL,
23
+ "ERROR": regscale_models.ChecklistStatus.FAIL,
24
+ "NOT APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
25
+ "NOT_APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
26
+ }
27
+
28
+ # Severity mapping for STIG findings
29
+ _SEVERITY_MAP = {
30
+ "critical": regscale_models.IssueSeverity.Critical,
31
+ "high": regscale_models.IssueSeverity.High,
32
+ "medium": regscale_models.IssueSeverity.Moderate,
33
+ "low": regscale_models.IssueSeverity.Low,
34
+ }
35
+
36
+
37
+ def _extract_field(pattern: str, text: str, flags: Union[int, re.RegexFlag] = 0, group: int = 1) -> str:
38
+ """
39
+ Extracts a field from a string using a regular expression.
40
+
41
+ :param str pattern: The regular expression pattern to search for
42
+ :param str text: The string to search in
43
+ :param int flags: Optional regular expression flags, defaults to 0
44
+ :param int group: The group number to return from the match, defaults to 1
45
+ :return: The extracted field as a string. Empty string if no match was found
46
+ :rtype: str
47
+ """
48
+ match = re.search(pattern, text, flags)
49
+ return match.group(group).strip() if match else ""
50
+
51
+
52
+ def _extract_reference_dict(output: str) -> dict:
53
+ """
54
+ Extract and parse reference information into dictionary.
55
+
56
+ :param str output: The STIG output string
57
+ :return: Dictionary of reference information
58
+ :rtype: dict
59
+ """
60
+ ref_info = _extract_field(r"Reference Information:\s*(.+)", output, flags=re.DOTALL)
61
+ ref_dict = {}
62
+ for item in ref_info.split(","):
63
+ if "|" in item:
64
+ parts = item.split("|", 1)
65
+ if len(parts) == 2:
66
+ ref_dict[parts[0].strip()] = parts[1].strip()
67
+ return ref_dict
68
+
69
+
70
+ def _map_result_to_status(result: str) -> regscale_models.ChecklistStatus:
71
+ """
72
+ Map result string to ChecklistStatus enum.
73
+
74
+ :param str result: The result string from STIG output
75
+ :return: ChecklistStatus enum value
76
+ :rtype: regscale_models.ChecklistStatus
77
+ """
78
+ result_key = result.upper().replace("_", " ").strip()
79
+ if result_key not in _RESULT_STATUS_MAP:
80
+ logger.warning("Result '%s' not found in status map", result)
81
+ return regscale_models.ChecklistStatus.NOT_REVIEWED
82
+ return _RESULT_STATUS_MAP[result_key]
83
+
84
+
85
+ def _map_severity_to_enum(severity: str) -> regscale_models.IssueSeverity:
86
+ """
87
+ Map severity string to IssueSeverity enum.
88
+
89
+ :param str severity: The severity string
90
+ :return: IssueSeverity enum value
91
+ :rtype: regscale_models.IssueSeverity
92
+ """
93
+ return _SEVERITY_MAP.get(severity.lower(), regscale_models.IssueSeverity.NotAssigned)
12
94
 
13
95
 
14
96
  def parse_stig_output(output: str, finding: IntegrationFinding) -> IntegrationFinding:
@@ -20,43 +102,39 @@ def parse_stig_output(output: str, finding: IntegrationFinding) -> IntegrationFi
20
102
  :return: An IntegrationFinding object containing the parsed finding information.
21
103
  :rtype: IntegrationFinding
22
104
  """
23
-
24
- def _extract_field(pattern: str, text: str, flags: Union[int, re.RegexFlag] = 0, group: int = 1) -> str:
25
- """
26
- Extracts a field from a string using a regular expression
27
-
28
- :param str pattern: The regular expression pattern to search for
29
- :param str text: The string to search in
30
- :param int flags: Optional regular expression flags, defaults to 0
31
- :param int group: The group number to return from the match, defaults to 1
32
- :return: The extracted field as a string. Empty string if no match was found
33
- :rtype: str
34
- """
35
- match = re.search(pattern, text, flags)
36
- return match.group(group).strip() if match else ""
37
-
38
- # Extract fields
39
- check_name_full = _extract_field(r"Check Name:\s*(.*?)\n", output, flags=re.DOTALL | re.MULTILINE)
105
+ # Extract fields using optimized regex patterns (preventing catastrophic backtracking)
106
+ check_name_full = _extract_field(r"Check Name:\s*([^\n]+)", output)
40
107
  check_name_parts = check_name_full.split(":", 1)
41
108
  rule_id = check_name_parts[0].strip()
42
109
  check_description = check_name_parts[1].strip() if len(check_name_parts) > 1 else ""
43
110
 
111
+ # Extract baseline from Target keyword
44
112
  baseline = _extract_field(r"(.*?)\s+Target\s+(.*)", check_description, group=2)
45
- if target_match := _extract_field(r"(.*?)\s+Target\s+(.*)", check_description):
113
+ target_match = _extract_field(r"(.*?)\s+Target\s+(.*)", check_description)
114
+ if target_match:
46
115
  check_description = check_description[: check_description.find(target_match) + len(target_match)].strip()
47
116
 
48
- information = _extract_field(r"Information:\s*(.*?)\n", output, flags=re.DOTALL | re.MULTILINE)
49
- vuln_discuss = _extract_field(r"VulnDiscussion='(.*?)'\s", output, flags=re.DOTALL | re.MULTILINE)
50
- result = _extract_field(r"Result:\s*(.*?)(?:\n|$)", output, flags=re.IGNORECASE | re.DOTALL)
51
- solution = _extract_field(r"Solution:\s*(.*?)\n\nReference Information:", output, flags=re.DOTALL | re.MULTILINE)
117
+ information = _extract_field(r"Information:\s*([^\n]+)", output)
118
+ vuln_discuss = _extract_field(r"VulnDiscussion='([^']+)'\s", output)
119
+ result = _extract_field(r"Result:\s*([^\n]+)", output, flags=re.IGNORECASE)
120
+ # Extract solution using a safe pattern that avoids backtracking
121
+ # Split on reference keyword and take the solution section
122
+ if _SOLUTION_KEYWORD in output and _REFERENCE_INFO_KEYWORD in output:
123
+ solution_start = output.find(_SOLUTION_KEYWORD) + len(_SOLUTION_KEYWORD)
124
+ ref_start = output.find(_REFERENCE_INFO_KEYWORD, solution_start)
125
+ solution = output[solution_start:ref_start].strip()
126
+ elif _SOLUTION_KEYWORD in output:
127
+ solution_start = output.find(_SOLUTION_KEYWORD) + len(_SOLUTION_KEYWORD)
128
+ solution = output[solution_start:].strip()
129
+ else:
130
+ solution = ""
52
131
 
53
- # Extract reference information
54
- ref_info = _extract_field(r"Reference Information:\s*(.*)", output, flags=re.DOTALL | re.MULTILINE)
55
- ref_dict = dict(item.split("|", 1) for item in ref_info.split(",") if "|" in item)
132
+ # Extract reference information using helper function
133
+ ref_dict = _extract_reference_dict(output)
56
134
 
57
135
  # Extract specific references
58
- cci_ref = ref_dict.get("CCI", "CCI-000366")
59
- severity = ref_dict.get("SEVERITY", "").lower()
136
+ cci_ref = ref_dict.get("CCI", DEFAULT_CCI_REF)
137
+ severity_str = ref_dict.get("SEVERITY", "").lower()
60
138
  oval_def = ref_dict.get("OVAL-DEF", "")
61
139
  generated_date = ref_dict.get("GENERATED-DATE", "")
62
140
  updated_date = ref_dict.get("UPDATED-DATE", "")
@@ -64,36 +142,19 @@ def parse_stig_output(output: str, finding: IntegrationFinding) -> IntegrationFi
64
142
  rule_id_full = ref_dict.get("RULE-ID", "")
65
143
  group_id = ref_dict.get("GROUP-ID", "")
66
144
 
145
+ # Extract vulnerability number
67
146
  vuln_num_match = re.search(r"SV-\d+r\d+_rule", rule_id)
68
- vuln_num = vuln_num_match.group(0) if vuln_num_match else "unknown"
147
+ vuln_num = vuln_num_match.group(0) if vuln_num_match else UNKNOWN_VULN_NUM
69
148
 
70
149
  title = f"{vuln_num}: {check_description}"
71
150
  issue_title = title
72
151
 
73
- status_map = {
74
- "PASSED": regscale_models.ChecklistStatus.PASS,
75
- "FAILED": regscale_models.ChecklistStatus.FAIL,
76
- "ERROR": regscale_models.ChecklistStatus.FAIL,
77
- "NOT APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
78
- "NOT_APPLICABLE": regscale_models.ChecklistStatus.NOT_APPLICABLE,
79
- }
152
+ # Map result to status using helper function
153
+ status = _map_result_to_status(result)
80
154
 
81
- result_key = result.upper().replace("_", " ").strip()
82
- if result_key not in status_map:
83
- logger.warning(f"Result '{result}' not found in status map")
84
- status = regscale_models.ChecklistStatus.NOT_REVIEWED
85
- else:
86
- status = status_map[result_key]
87
-
88
- # Map severity to IssueSeverity enum
89
- priority = (severity or "").title()
90
- severity_map = {
91
- "critical": regscale_models.IssueSeverity.Critical,
92
- "high": regscale_models.IssueSeverity.High,
93
- "medium": regscale_models.IssueSeverity.Moderate,
94
- "low": regscale_models.IssueSeverity.Low,
95
- }
96
- severity = severity_map.get(severity.lower(), regscale_models.IssueSeverity.NotAssigned)
155
+ # Map severity to IssueSeverity enum using helper function
156
+ priority = (severity_str or "").title()
157
+ severity = _map_severity_to_enum(severity_str)
97
158
 
98
159
  results = (
99
160
  f"Vulnerability Number: {vuln_num}, Severity: {severity.value}, "
@@ -132,9 +193,4 @@ def parse_stig_output(output: str, finding: IntegrationFinding) -> IntegrationFi
132
193
  finding.rule_id_full = rule_id_full
133
194
  finding.group_id = group_id
134
195
 
135
- # Future values
136
- # finding.comments = ""
137
- # finding.poam_comments = ""
138
- # finding.rule_version = ""
139
-
140
196
  return finding
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
5
5
  from typing import Optional, Dict, Any, List
6
6
 
7
7
  from regscale.core.app.utils.app_utils import error_and_exit, check_file_path
8
- from regscale.integrations.commercial.wizv2.constants import CONTENT_TYPE
8
+ from regscale.integrations.commercial.wizv2.core.constants import CONTENT_TYPE
9
9
  from regscale.core.app.application import Application
10
10
  from regscale.utils import PaginatedGraphQLClient
11
11
 
@@ -25,7 +25,7 @@ def wiz():
25
25
  @click.option("--client_secret", default=None, hide_input=True, required=False) # type: ignore
26
26
  def authenticate(client_id, client_secret):
27
27
  """Authenticate to Wiz."""
28
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
28
+ from regscale.integrations.commercial.wizv2.core.auth import wiz_authenticate
29
29
 
30
30
  wiz_authenticate(client_id, client_secret)
31
31
 
@@ -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
  )
@@ -136,13 +136,13 @@ def issues(
136
136
  Process Issues from Wiz into RegScale
137
137
  """
138
138
  from regscale.core.app.utils.app_utils import check_license
139
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
139
+ from regscale.integrations.commercial.wizv2.core.auth import wiz_authenticate
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
  )
@@ -191,12 +191,12 @@ def attach_sbom(
191
191
  standard="CycloneDX",
192
192
  ):
193
193
  """Download SBOMs from a Wiz report by ID and add them to the corresponding RegScale assets."""
194
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
194
+ from regscale.integrations.commercial.wizv2.core.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(
@@ -212,15 +212,6 @@ def attach_sbom(
212
212
  )
213
213
 
214
214
 
215
- @wiz.command()
216
- def threats():
217
- """Process threats from Wiz -> Coming soon"""
218
- from regscale.core.app.utils.app_utils import check_license
219
-
220
- check_license()
221
- logger.info("Threats - COMING SOON")
222
-
223
-
224
215
  @wiz.command()
225
216
  @click.option( # type: ignore
226
217
  "--wiz_project_id",
@@ -234,7 +225,7 @@ def threats():
234
225
  "--client_id",
235
226
  "-ci",
236
227
  help="Wiz Client ID, or can be set as environment variable wizClientId",
237
- default="",
228
+ default=None,
238
229
  hide_input=False,
239
230
  required=False,
240
231
  )
@@ -242,7 +233,7 @@ def threats():
242
233
  "--client_secret",
243
234
  "-cs",
244
235
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
245
- default="",
236
+ default=None,
246
237
  hide_input=True,
247
238
  required=False,
248
239
  )
@@ -266,9 +257,9 @@ def vulnerabilities(
266
257
  """Process vulnerabilities from Wiz"""
267
258
  from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
268
259
 
269
- if not client_secret:
260
+ if client_secret is None:
270
261
  client_secret = WizVariables.wizClientSecret
271
- if not client_id:
262
+ if client_id is None:
272
263
  client_id = WizVariables.wizClientId
273
264
 
274
265
  scanner = WizVulnerabilityIntegration(plan_id=regscale_ssp_id)
@@ -286,7 +277,7 @@ def vulnerabilities(
286
277
  "--client_id",
287
278
  "-ci",
288
279
  help="Wiz Client ID, or can be set as environment variable wizClientId",
289
- default="",
280
+ default=None,
290
281
  hide_input=False,
291
282
  required=False,
292
283
  )
@@ -294,14 +285,18 @@ def vulnerabilities(
294
285
  "--client_secret",
295
286
  "-cs",
296
287
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
297
- default="",
288
+ default=None,
298
289
  hide_input=True,
299
290
  required=False,
300
291
  )
301
292
  @click.option("--evidence_id", "-e", help="Wiz Evidence ID", required=True, type=int) # type: ignore
302
293
  @click.option("--report_id", "-r", help="Wiz Report ID", required=True) # type: ignore
303
- @click.option("--report_file_name", "-n", help="Report file name", default="evidence_report", required=False) # type: ignore
304
- @click.option("--report_file_extension", "-e", help="Report file extension", default="csv", required=False) # type: ignore
294
+ @click.option(
295
+ "--report_file_name", "-n", help="Report file name", default="evidence_report", required=False
296
+ ) # type: ignore
297
+ @click.option(
298
+ "--report_file_extension", "-e", help="Report file extension", default="csv", required=False
299
+ ) # type: ignore
305
300
  def add_report_evidence(
306
301
  client_id,
307
302
  client_secret,
@@ -311,12 +306,12 @@ def add_report_evidence(
311
306
  report_file_extension: str = "csv",
312
307
  ):
313
308
  """Download a Wiz report by ID and Attach to Evidence locker"""
314
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
309
+ from regscale.integrations.commercial.wizv2.core.auth import wiz_authenticate
315
310
  from regscale.integrations.commercial.wizv2.utils import fetch_report_by_id
316
311
 
317
- if not client_secret:
312
+ if client_secret is None:
318
313
  client_secret = WizVariables.wizClientSecret
319
- if not client_id:
314
+ if client_id is None:
320
315
  client_id = WizVariables.wizClientId
321
316
 
322
317
  wiz_authenticate(
@@ -324,7 +319,10 @@ def add_report_evidence(
324
319
  client_secret=client_secret,
325
320
  )
326
321
  fetch_report_by_id(
327
- report_id, parent_id=evidence_id, report_file_name=report_file_name, report_file_extension=report_file_extension
322
+ report_id,
323
+ parent_id=evidence_id,
324
+ report_file_name=report_file_name,
325
+ report_file_extension=report_file_extension,
328
326
  )
329
327
 
330
328
 
@@ -346,7 +344,7 @@ def add_report_evidence(
346
344
  "--client_id",
347
345
  "-i",
348
346
  help="Wiz Client ID, or can be set as environment variable wizClientId",
349
- default="",
347
+ default=None,
350
348
  hide_input=False,
351
349
  required=False,
352
350
  )
@@ -354,14 +352,17 @@ def add_report_evidence(
354
352
  "--client_secret",
355
353
  "-s",
356
354
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
357
- default="",
355
+ default=None,
358
356
  hide_input=True,
359
357
  required=False,
360
358
  )
361
359
  @click.option( # type: ignore
362
360
  "--framework_id",
363
361
  "-f",
364
- help="Wiz framework ID or shorthand (e.g., 'nist', 'aws', 'wf-id-4'). Use --list-frameworks to see options. Default: wf-id-4 (NIST SP 800-53 Rev 5)",
362
+ help=(
363
+ "Wiz framework ID or shorthand (e.g., 'nist', 'aws', 'wf-id-4'). "
364
+ "Use --list-frameworks to see options. Default: wf-id-4 (NIST SP 800-53 Rev 5)"
365
+ ),
365
366
  default="wf-id-4",
366
367
  required=False,
367
368
  )
@@ -420,67 +421,51 @@ def sync_compliance(
420
421
  """
421
422
  Sync policy compliance assessments from Wiz to RegScale.
422
423
 
423
- This command fetches policy assessment data from Wiz and creates:
424
+ This command now uses CSV compliance reports from Wiz instead of GraphQL API.
425
+ It creates:
424
426
  - Control assessments based on policy compliance results
425
427
  - Issues for failed policy assessments (if --create-issues enabled)
426
428
  - Updates to control implementation status (if --update-control-status enabled)
427
- - JSON output file with policy compliance data in artifacts/wiz/ directory
428
- - Cached framework mapping for improved performance
429
429
 
430
- CACHING:
431
- By default, the command will reuse cached policy data if it's newer than the cache
432
- duration (default: 60 minutes). Use --refresh to force fresh data from Wiz.
433
- Use --cache-duration to adjust how long cached data is considered valid.
430
+ NOTE: This command is deprecated. Use 'compliance_report' command instead.
434
431
  """
435
- from regscale.integrations.commercial.wizv2.policy_compliance import (
436
- WizPolicyComplianceIntegration,
437
- list_available_frameworks,
438
- resolve_framework_id,
439
- )
432
+ click.echo("⚠️ sync_compliance is deprecated and now uses compliance_report implementation.")
433
+ click.echo(" Consider using 'regscale wiz compliance_report' directly for future use.\n")
440
434
 
441
- # Handle --list-frameworks flag
435
+ # Handle --list-frameworks flag (no longer supported, inform user)
442
436
  if list_frameworks:
443
- click.echo(list_available_frameworks())
437
+ click.echo("❌ --list-frameworks is no longer supported in the deprecated sync_compliance command.")
438
+ click.echo(" Please use 'regscale wiz compliance_report' instead.\n")
444
439
  return
445
440
 
446
441
  # Use environment variables if not provided
447
- if not client_secret:
442
+ if client_secret is None or client_secret == "":
448
443
  client_secret = WizVariables.wizClientSecret
449
- if not client_id:
444
+ if client_id is None or client_id == "":
450
445
  client_id = WizVariables.wizClientId
451
446
 
452
- # Resolve framework ID using the enhanced framework resolution
453
- try:
454
- resolved_framework_id = resolve_framework_id(framework_id.lower())
455
- if resolved_framework_id != framework_id:
456
- from regscale.integrations.commercial.wizv2.policy_compliance import FRAMEWORK_MAPPINGS
457
-
458
- framework_name = FRAMEWORK_MAPPINGS.get(resolved_framework_id, resolved_framework_id)
459
- click.echo(f"🔍 Resolved '{framework_id}' to '{framework_name}' ({resolved_framework_id})")
460
- except ValueError as e:
461
- click.echo(f"❌ {str(e)}")
462
- click.echo("\nUse --list-frameworks to see all available options.")
463
- return
447
+ # Import compliance_report processor instead of GraphQL-based integration
448
+ from regscale.integrations.commercial.wizv2.compliance_report import WizComplianceReportProcessor
464
449
 
465
- # Create and run the policy compliance integration
466
- policy_integration = WizPolicyComplianceIntegration(
450
+ # Create processor with similar options to compliance_report command
451
+ processor = WizComplianceReportProcessor(
467
452
  plan_id=regscale_id,
468
453
  wiz_project_id=wiz_project_id,
469
454
  client_id=client_id,
470
455
  client_secret=client_secret,
471
- framework_id=resolved_framework_id,
472
456
  regscale_module=regscale_module,
473
457
  create_poams=create_poams,
474
- cache_duration_minutes=cache_duration,
475
- force_refresh=refresh,
476
- )
477
-
478
- # Run the policy compliance sync
479
- policy_integration.sync_policy_compliance(
480
458
  create_issues=create_issues,
481
459
  update_control_status=update_control_status,
460
+ report_file_path=None, # Will create new report
461
+ force_fresh_report=refresh, # Map --refresh to force_fresh_report
462
+ reuse_existing_reports=not refresh, # Inverse of refresh
463
+ bypass_control_filtering=True, # Enable for performance
482
464
  )
483
465
 
466
+ # Process the compliance report using new ComplianceIntegration pattern
467
+ processor.process_compliance_sync()
468
+
484
469
 
485
470
  @wiz.command(name="compliance_report")
486
471
  @click.option(
@@ -496,7 +481,7 @@ def sync_compliance(
496
481
  "--client_id",
497
482
  "-i",
498
483
  help="Wiz Client ID, or can be set as environment variable wizClientId",
499
- default="",
484
+ default=None,
500
485
  hide_input=False,
501
486
  required=False,
502
487
  )
@@ -504,7 +489,7 @@ def sync_compliance(
504
489
  "--client_secret",
505
490
  "-s",
506
491
  help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
507
- default="",
492
+ default=None,
508
493
  hide_input=True,
509
494
  required=False,
510
495
  )
@@ -581,7 +566,7 @@ def compliance_report(
581
566
  """
582
567
  from regscale.integrations.commercial.wizv2.compliance_report import WizComplianceReportProcessor
583
568
 
584
- # Use environment variables if not provided
569
+ # Use environment variables if not provided or empty
585
570
  if not client_secret:
586
571
  client_secret = WizVariables.wizClientSecret
587
572
  if not client_id:
@@ -0,0 +1,15 @@
1
+ """Compliance-specific functionality for Wiz integration."""
2
+
3
+ from regscale.integrations.commercial.wizv2.compliance.helpers import (
4
+ AssetConsolidator,
5
+ ControlAssessmentProcessor,
6
+ ControlImplementationCache,
7
+ IssueFieldSetter,
8
+ )
9
+
10
+ __all__ = [
11
+ "AssetConsolidator",
12
+ "ControlAssessmentProcessor",
13
+ "ControlImplementationCache",
14
+ "IssueFieldSetter",
15
+ ]