regscale-cli 6.21.0.0__py3-none-any.whl → 6.21.2.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.
Files changed (54) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/application.py +7 -0
  3. regscale/integrations/commercial/__init__.py +9 -10
  4. regscale/integrations/commercial/amazon/common.py +79 -2
  5. regscale/integrations/commercial/aws/cli.py +183 -9
  6. regscale/integrations/commercial/aws/scanner.py +544 -9
  7. regscale/integrations/commercial/cpe.py +18 -1
  8. regscale/integrations/commercial/import_all/import_all_cmd.py +2 -2
  9. regscale/integrations/commercial/microsoft_defender/__init__.py +0 -0
  10. regscale/integrations/commercial/{defender.py → microsoft_defender/defender.py} +38 -612
  11. regscale/integrations/commercial/microsoft_defender/defender_api.py +286 -0
  12. regscale/integrations/commercial/microsoft_defender/defender_constants.py +80 -0
  13. regscale/integrations/commercial/microsoft_defender/defender_scanner.py +168 -0
  14. regscale/integrations/commercial/qualys/__init__.py +24 -86
  15. regscale/integrations/commercial/qualys/containers.py +2 -0
  16. regscale/integrations/commercial/qualys/scanner.py +7 -2
  17. regscale/integrations/commercial/sonarcloud.py +110 -71
  18. regscale/integrations/commercial/tenablev2/jsonl_scanner.py +2 -1
  19. regscale/integrations/commercial/wizv2/async_client.py +10 -3
  20. regscale/integrations/commercial/wizv2/click.py +105 -26
  21. regscale/integrations/commercial/wizv2/constants.py +249 -1
  22. regscale/integrations/commercial/wizv2/data_fetcher.py +401 -0
  23. regscale/integrations/commercial/wizv2/finding_processor.py +295 -0
  24. regscale/integrations/commercial/wizv2/issue.py +2 -2
  25. regscale/integrations/commercial/wizv2/parsers.py +3 -2
  26. regscale/integrations/commercial/wizv2/policy_compliance.py +3057 -0
  27. regscale/integrations/commercial/wizv2/policy_compliance_helpers.py +564 -0
  28. regscale/integrations/commercial/wizv2/scanner.py +19 -25
  29. regscale/integrations/commercial/wizv2/utils.py +258 -85
  30. regscale/integrations/commercial/wizv2/variables.py +4 -3
  31. regscale/integrations/compliance_integration.py +1607 -0
  32. regscale/integrations/public/fedramp/fedramp_five.py +93 -8
  33. regscale/integrations/public/fedramp/markdown_parser.py +7 -1
  34. regscale/integrations/scanner_integration.py +57 -6
  35. regscale/models/__init__.py +1 -1
  36. regscale/models/app_models/__init__.py +1 -0
  37. regscale/models/integration_models/cisa_kev_data.json +103 -4
  38. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  39. regscale/{integrations/commercial/wizv2/models.py → models/integration_models/wizv2.py} +4 -12
  40. regscale/models/regscale_models/file.py +4 -0
  41. regscale/models/regscale_models/issue.py +151 -8
  42. regscale/models/regscale_models/regscale_model.py +4 -2
  43. regscale/models/regscale_models/security_plan.py +1 -1
  44. regscale/utils/graphql_client.py +3 -1
  45. {regscale_cli-6.21.0.0.dist-info → regscale_cli-6.21.2.0.dist-info}/METADATA +9 -9
  46. {regscale_cli-6.21.0.0.dist-info → regscale_cli-6.21.2.0.dist-info}/RECORD +52 -44
  47. tests/regscale/core/test_version_regscale.py +5 -3
  48. tests/regscale/integrations/test_wiz_policy_compliance_affected_controls.py +154 -0
  49. tests/regscale/test_authorization.py +0 -65
  50. tests/regscale/test_init.py +0 -96
  51. {regscale_cli-6.21.0.0.dist-info → regscale_cli-6.21.2.0.dist-info}/LICENSE +0 -0
  52. {regscale_cli-6.21.0.0.dist-info → regscale_cli-6.21.2.0.dist-info}/WHEEL +0 -0
  53. {regscale_cli-6.21.0.0.dist-info → regscale_cli-6.21.2.0.dist-info}/entry_points.txt +0 -0
  54. {regscale_cli-6.21.0.0.dist-info → regscale_cli-6.21.2.0.dist-info}/top_level.txt +0 -0
@@ -24,14 +24,15 @@ from regscale.core.app.utils.app_utils import (
24
24
  error_and_exit,
25
25
  check_file_path,
26
26
  get_current_datetime,
27
- format_dict_to_html,
28
27
  create_progress_object,
29
28
  )
30
29
  from regscale.core.utils.date import datetime_obj
30
+ from regscale.integrations.commercial.cpe import extract_product_name_and_version
31
31
  from regscale.integrations.commercial.wizv2.constants import (
32
32
  BEARER,
33
33
  CHECK_INTERVAL_FOR_DOWNLOAD_REPORT,
34
34
  CONTENT_TYPE,
35
+ CPE_PART_TO_CATEGORY_MAPPING,
35
36
  CREATE_REPORT_QUERY,
36
37
  DOWNLOAD_QUERY,
37
38
  MAX_RETRIES,
@@ -39,7 +40,7 @@ from regscale.integrations.commercial.wizv2.constants import (
39
40
  REPORTS_QUERY,
40
41
  RERUN_REPORT_QUERY,
41
42
  )
42
- from regscale.integrations.commercial.wizv2.models import ComplianceReport, ComplianceCheckStatus
43
+ from regscale.models.integration_models.wizv2 import ComplianceReport, ComplianceCheckStatus
43
44
  from regscale.integrations.commercial.wizv2.variables import WizVariables
44
45
  from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
45
46
  from regscale.models import (
@@ -151,36 +152,49 @@ def create_asset_type(asset_type: str) -> str:
151
152
  return asset_type
152
153
 
153
154
 
154
- def map_category(asset_string: Union[set[str], str]) -> regscale_models.AssetCategory:
155
+ def map_category(node: dict[str, Any]) -> regscale_models.AssetCategory:
155
156
  """
156
- Map the asset category based on the asset string. If the asset string is not found in the wizHardwareAssetTypes or
157
- in the AssetCategory enum, it will be mapped to "Software"
157
+ Map the asset category based on the given node. The node should be a CloudResoruce response from
158
+ the Wiz inventory query.
158
159
 
159
- :param Union[set[str], str] asset_string: Set of strings from the Wiz asset's technologies.deploymentModel or
160
- the node's type
160
+ If the CloudResource type or any of the technologies deploymentModel entries match those in the
161
+ config parameter wizHardwareAssetTypes, "Hardware" will be returned, otherwise "Software".
162
+
163
+ :param dict[str, Any] node: A single node results from the Wiz CloudResource invenotty query.
164
+ It should have a 'type' key with the string asset type and a 'graphEntity' eky with a dict
165
+ which, in turn, has a 'technologies' key whose value is a dict with a 'deploymentModel' key
166
+ with a string value.
161
167
  :return: RegScale AssetCategory
162
168
  :rtype: regscale_models.AssetCategory
163
169
  """
164
- try:
165
- if isinstance(asset_string, set):
166
- hardware_count = sum(
167
- asset.lower() == type.lower() for type in WizVariables.wizHardwareAssetTypes for asset in asset_string
168
- )
169
- software_count = len(asset_string) - hardware_count
170
- return (
171
- regscale_models.AssetCategory.Hardware
172
- if hardware_count > software_count
173
- else regscale_models.AssetCategory.Software
174
- )
175
- if asset_string in WizVariables.wizHardwareAssetTypes:
170
+
171
+ # First check if there is a CPE which can tell us the category directly.
172
+ cpe = node.get("graphEntity", {}).get("properties", {}).get("cpe", "")
173
+ cpe_part = extract_product_name_and_version(cpe).get("part", "")
174
+ if cpe_part and cpe_part.lower() in CPE_PART_TO_CATEGORY_MAPPING:
175
+ return CPE_PART_TO_CATEGORY_MAPPING[cpe_part]
176
+
177
+ # Then try mapping by the configured Wiz hardware asset and technology deployment model types.
178
+ asset_type = node.get("type", "")
179
+ if WizVariables.useWizHardwareAssetTypes:
180
+ if asset_type in WizVariables.wizHardwareAssetTypes:
176
181
  return regscale_models.AssetCategory.Hardware
177
- elif asset_category := getattr(regscale_models.AssetCategory, asset_string):
182
+ if (graph_entity := node.get("graphEntity", {})) and (techs := graph_entity.get("technologies", [])):
183
+ for tech in techs:
184
+ # We double check just in case we get an explicit None for the technologies.
185
+ if tech and tech.get("deploymentModel", None) in WizVariables.wizHardwareAssetTypes:
186
+ return regscale_models.AssetCategory.Hardware
187
+ else:
188
+ logger.debug("No graphEntity set for node %r, default to Software.", node)
189
+
190
+ # Finally try matching the asset type directly by name.
191
+ if hasattr(regscale_models.AssetCategory, asset_type):
192
+ if asset_category := getattr(regscale_models.AssetCategory, asset_type):
178
193
  return asset_category
179
- return regscale_models.AssetCategory.Software
180
- except (KeyError, AttributeError) as ex:
181
- # why map AssetCategory of everything is software?
182
- logger.debug("Unable to find %s in AssetType enum. Defaulting to Software\n", ex)
183
- return regscale_models.AssetCategory.Software
194
+ logger.debug("Unknown AssetType %r for node %r. Defaulting to Software.", asset_type, node)
195
+
196
+ # If all else fails, default to software.
197
+ return regscale_models.AssetCategory.Software
184
198
 
185
199
 
186
200
  def convert_first_seen_to_days(first_seen: str) -> int:
@@ -674,9 +688,44 @@ def create_compliance_report(
674
688
  "type": "COMPLIANCE_ASSESSMENTS",
675
689
  "csvDelimiter": "US",
676
690
  "projectId": wiz_project_id,
677
- "complianceAssessmentsParams": {"securityFrameworkIds": [framework_id]},
691
+ "complianceAssessmentsParams": {
692
+ "securityFrameworkIds": [framework_id],
693
+ },
678
694
  "emailTargetParams": None,
679
695
  "exportDestinations": None,
696
+ "columnSelection": [
697
+ "Assessed At",
698
+ "Category",
699
+ "Cloud Provider",
700
+ "Cloud Provider ID",
701
+ "Compliance Check Name (Wiz Subcategory)",
702
+ "Created At",
703
+ "Framework",
704
+ "Ignore Reason",
705
+ "Issue/Finding ID",
706
+ "Native Type",
707
+ "Object Type",
708
+ "Policy Description",
709
+ "Policy ID",
710
+ "Policy Name",
711
+ "Policy Short Name",
712
+ "Policy Type",
713
+ "Projects",
714
+ "Remediation Steps",
715
+ "Resource Cloud Platform",
716
+ "Resource Group Name",
717
+ "Resource ID",
718
+ "Resource Name",
719
+ "Resource Region",
720
+ "Result",
721
+ "Severity",
722
+ "Subscription",
723
+ "Subscription Name",
724
+ "Subscription Provider ID",
725
+ "Subscription Status",
726
+ "Tags",
727
+ "Updated At",
728
+ ],
680
729
  }
681
730
  }
682
731
 
@@ -749,6 +798,8 @@ def rerun_expired_report(variables: Dict) -> requests.Response:
749
798
  return response
750
799
 
751
800
 
801
+ # Compliance functions moved to wiz_compliance.py
802
+ # This is a deprecated function - use WizComplianceIntegration instead
752
803
  def _sync_compliance(
753
804
  wiz_project_id: str,
754
805
  regscale_id: int,
@@ -757,6 +808,7 @@ def _sync_compliance(
757
808
  client_secret: str,
758
809
  catalog_id: Optional[int] = None,
759
810
  framework: Optional[str] = "NIST800-53R5",
811
+ update_control_status: bool = True,
760
812
  ) -> List[ComplianceReport]:
761
813
  """
762
814
  Sync compliance posture from Wiz to RegScale
@@ -768,6 +820,7 @@ def _sync_compliance(
768
820
  :param str client_secret: Wiz Client Secret
769
821
  :param Optional[int] catalog_id: Catalog ID, defaults to None
770
822
  :param Optional[str] framework: Framework, defaults to NIST800-53R5
823
+ :param bool update_control_status: Update control implementation status based on compliance results, defaults to True
771
824
  :return: List of ComplianceReport objects
772
825
  :rtype: List[ComplianceReport]
773
826
  """
@@ -851,6 +904,7 @@ def _sync_compliance(
851
904
  controls=controls,
852
905
  progress=compliance_job_progress,
853
906
  task=saving_regscale_data_job,
907
+ update_control_status=update_control_status,
854
908
  )
855
909
  logger.info("Completed saving RegScale data.")
856
910
  except Exception:
@@ -922,7 +976,13 @@ def _clean_passing_list(passing: Dict, failing: Dict) -> None:
922
976
 
923
977
 
924
978
  def create_assessment_from_compliance_report(
925
- controls_to_reports: Dict, regscale_id: int, regscale_module: str, controls: List, progress: Progress, task: TaskID
979
+ controls_to_reports: Dict,
980
+ regscale_id: int,
981
+ regscale_module: str,
982
+ controls: List,
983
+ progress: Progress,
984
+ task: TaskID,
985
+ update_control_status: bool = True,
926
986
  ) -> None:
927
987
  """
928
988
  Create assessment from compliance report
@@ -933,6 +993,7 @@ def create_assessment_from_compliance_report(
933
993
  :param List controls: Controls
934
994
  :param Progress progress: Progress object, used for progress bar updates
935
995
  :param TaskID task: Task ID, used for progress bar updates
996
+ :param bool update_control_status: Update control implementation status based on compliance results, defaults to True
936
997
  :return: None
937
998
  :rtype: None
938
999
  """
@@ -954,7 +1015,12 @@ def create_assessment_from_compliance_report(
954
1015
  filtered_results = [x for x in implementations if x.controlID == control_record_id]
955
1016
 
956
1017
  start_time = time.time()
957
- create_report_assessment(filtered_results, reports, control_id)
1018
+ create_report_assessment(
1019
+ filtered_results,
1020
+ reports,
1021
+ control_id,
1022
+ update_control_status=update_control_status,
1023
+ )
958
1024
  end_time = time.time()
959
1025
  logger.debug(f"Assessment creation for {control_id} took {end_time - start_time:.2f} seconds")
960
1026
 
@@ -967,13 +1033,19 @@ def create_assessment_from_compliance_report(
967
1033
  progress.update(task, advance=1)
968
1034
 
969
1035
 
970
- def create_report_assessment(filtered_results: List, reports: List, control_id: str) -> None:
1036
+ def create_report_assessment(
1037
+ filtered_results: List,
1038
+ reports: List,
1039
+ control_id: str,
1040
+ update_control_status: bool = True,
1041
+ ) -> None:
971
1042
  """
972
1043
  Create a single aggregated report assessment per control
973
1044
 
974
1045
  :param List filtered_results: Filtered results
975
1046
  :param List reports: List of ComplianceReport objects for this control
976
1047
  :param str control_id: Control ID
1048
+ :param bool update_control_status: Update control implementation status based on compliance results, defaults to True
977
1049
  :return: None
978
1050
  :rtype: None
979
1051
  """
@@ -1042,11 +1114,15 @@ def create_report_assessment(filtered_results: List, reports: List, control_id:
1042
1114
  isPublic=True,
1043
1115
  ).create()
1044
1116
 
1045
- # Update implementation status once with aggregated result
1046
- update_implementation_status(
1047
- implementation=implementation,
1048
- result=overall_result,
1049
- )
1117
+ # Update implementation status once with aggregated result (if enabled)
1118
+ if update_control_status:
1119
+ update_implementation_status(
1120
+ implementation=implementation,
1121
+ result=overall_result,
1122
+ )
1123
+ logger.debug(f"Updated implementation status for {control_id}: {overall_result}")
1124
+ else:
1125
+ logger.debug(f"Skipping implementation status update for {control_id} (disabled via parameter)")
1050
1126
 
1051
1127
  logger.info(
1052
1128
  f"Created aggregated assessment for {control_id}: {assessment.id} "
@@ -1188,76 +1264,173 @@ def report_result_to_implementation_status(result: str) -> str:
1188
1264
  compliance_settings = get_wiz_compliance_settings()
1189
1265
 
1190
1266
  if compliance_settings:
1191
- status = _get_status_from_compliance_settings(result, compliance_settings)
1192
- if status:
1193
- return status
1267
+ try:
1268
+ # Get implementation status labels from compliance settings
1269
+ status_labels = compliance_settings.get_field_labels("implementationStatus")
1270
+
1271
+ # Map compliance check result to implementation status
1272
+ result_lower = result.lower()
1273
+ for label in status_labels:
1274
+ label_lower = label.lower()
1275
+ if result_lower == ComplianceCheckStatus.PASS.value.lower():
1276
+ if label_lower in ["implemented", "complete", "compliant"]:
1277
+ return label
1278
+ elif result_lower == ComplianceCheckStatus.FAIL.value.lower():
1279
+ if label_lower in ["inremediation", "in remediation", "remediation", "failed", "non-compliant"]:
1280
+ return label
1281
+ else: # Not implemented or other status
1282
+ if label_lower in ["notimplemented", "not implemented", "pending", "planned"]:
1283
+ return label
1284
+
1285
+ logger.debug(f"No matching compliance setting found for result: {result}")
1286
+ except Exception as e:
1287
+ logger.debug(f"Error using compliance settings for implementation status mapping: {e}")
1194
1288
 
1195
1289
  # Fallback to default mapping
1196
- return _get_default_status_mapping(result)
1290
+ if result == ComplianceCheckStatus.PASS.value:
1291
+ return ControlImplementationStatus.Implemented.value
1292
+ elif result == ComplianceCheckStatus.FAIL.value:
1293
+ return ControlImplementationStatus.InRemediation.value
1294
+ else:
1295
+ return ControlImplementationStatus.NotImplemented.value
1197
1296
 
1198
1297
 
1199
- def _get_status_from_compliance_settings(result: str, compliance_settings) -> Optional[str]:
1298
+ def create_vulnerabilities_from_wiz_findings(
1299
+ wiz_project_id: str,
1300
+ regscale_plan_id: int,
1301
+ client_id: Optional[str] = None,
1302
+ client_secret: Optional[str] = None,
1303
+ filter_by_override: Optional[str] = None,
1304
+ ) -> int:
1305
+ """
1306
+ Create vulnerabilities from Wiz findings using the WizVulnerabilityIntegration class.
1307
+
1308
+ This function properly uses the ScannerIntegration framework to create vulnerabilities
1309
+ and associated mappings, following the established patterns.
1310
+
1311
+ :param str wiz_project_id: Wiz project ID to scan
1312
+ :param int regscale_plan_id: RegScale security plan ID
1313
+ :param Optional[str] client_id: Wiz client ID (optional, uses WizVariables if not provided)
1314
+ :param Optional[str] client_secret: Wiz client secret (optional, uses WizVariables if not provided)
1315
+ :param Optional[str] filter_by_override: Optional filter override for findings
1316
+ :return: Number of vulnerabilities processed
1317
+ :rtype: int
1200
1318
  """
1201
- Get implementation status from compliance settings
1319
+ # Import here to avoid circular imports
1320
+ from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
1202
1321
 
1203
- :param str result: Report result
1204
- :param compliance_settings: Compliance settings object
1205
- :return: Implementation status or None if not found
1206
- :rtype: Optional[str]
1207
- """
1208
1322
  try:
1209
- status_labels = compliance_settings.get_field_labels("implementationStatus")
1210
- result_lower = result.lower()
1323
+ logger.info(f"Starting vulnerability creation for Wiz project {wiz_project_id}")
1211
1324
 
1212
- for label in status_labels:
1213
- status = _match_result_to_label(result_lower, label)
1214
- if status:
1215
- return status
1325
+ # Create the integration instance
1326
+ wiz_integration = WizVulnerabilityIntegration(plan_id=regscale_plan_id)
1216
1327
 
1217
- logger.debug(f"No matching compliance setting found for result: {result}")
1218
- return None
1328
+ # Authenticate if credentials provided
1329
+ if client_id and client_secret:
1330
+ wiz_integration.authenticate(client_id=client_id, client_secret=client_secret)
1331
+ elif not WizVariables.wizAccessToken:
1332
+ # Try to authenticate with stored credentials
1333
+ wiz_integration.authenticate()
1334
+
1335
+ # Set up any filter overrides
1336
+ if filter_by_override:
1337
+ try:
1338
+ filter_dict = json.loads(filter_by_override)
1339
+ logger.info(f"Using filter override: {filter_dict}")
1340
+ except json.JSONDecodeError:
1341
+ logger.warning(f"Invalid filter override JSON: {filter_by_override}")
1342
+ filter_dict = {}
1343
+ else:
1344
+ filter_dict = {"projectId": wiz_project_id}
1345
+
1346
+ # Use the sync_findings class method which handles the complete workflow:
1347
+ # 1. Creates ScanHistory
1348
+ # 2. Fetches findings from Wiz
1349
+ # 3. Creates vulnerabilities and vulnerability mappings
1350
+ # 4. Creates associated issues (if configured)
1351
+ # 5. Closes outdated vulnerabilities
1352
+ # 6. Updates scan history with results
1353
+ vulnerabilities_processed = WizVulnerabilityIntegration.sync_findings(
1354
+ plan_id=regscale_plan_id,
1355
+ wiz_project_id=wiz_project_id,
1356
+ filter_by_override=json.dumps(filter_dict) if filter_dict else None,
1357
+ )
1358
+
1359
+ logger.info(f"Successfully processed {vulnerabilities_processed} vulnerabilities from Wiz")
1360
+ return vulnerabilities_processed
1219
1361
 
1220
1362
  except Exception as e:
1221
- logger.debug(f"Error using compliance settings for implementation status mapping: {e}")
1222
- return None
1363
+ logger.error(f"Error creating vulnerabilities from Wiz findings: {e}", exc_info=True)
1364
+ raise
1223
1365
 
1224
1366
 
1225
- def _match_result_to_label(result_lower: str, label: str) -> Optional[str]:
1367
+ def create_single_vulnerability_from_wiz_data(
1368
+ wiz_finding_data: Dict[str, Any],
1369
+ asset_id: str,
1370
+ regscale_plan_id: int,
1371
+ scan_history_id: Optional[int] = None,
1372
+ ) -> Optional[regscale_models.Vulnerability]:
1226
1373
  """
1227
- Match a result to a status label based on predefined mappings
1374
+ Create a single vulnerability from Wiz finding data.
1375
+
1376
+ This is a lower-level function for creating individual vulnerabilities when you have
1377
+ specific Wiz finding data and want more control over the process.
1228
1378
 
1229
- :param str result_lower: Lowercase result string
1230
- :param str label: Status label to check
1231
- :return: Matched label or None
1232
- :rtype: Optional[str]
1379
+ :param Dict[str, Any] wiz_finding_data: Raw Wiz finding data
1380
+ :param str asset_id: Asset identifier for the vulnerability
1381
+ :param int regscale_plan_id: RegScale security plan ID
1382
+ :param Optional[int] scan_history_id: Scan history ID (creates new if not provided)
1383
+ :return: Created vulnerability or None if creation failed
1384
+ :rtype: Optional[regscale_models.Vulnerability]
1233
1385
  """
1234
- label_lower = label.lower()
1386
+ # Import here to avoid circular imports
1387
+ from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
1388
+ from regscale.integrations.commercial.wizv2.constants import WizVulnerabilityType
1235
1389
 
1236
- if result_lower == ComplianceCheckStatus.PASS.value.lower():
1237
- return label if label_lower in ["implemented", "complete", "compliant"] else None
1390
+ try:
1391
+ # Create integration instance
1392
+ wiz_integration = WizVulnerabilityIntegration(plan_id=regscale_plan_id)
1393
+
1394
+ # Create or get scan history
1395
+ if scan_history_id:
1396
+ scan_history = regscale_models.ScanHistory.get_by_id(scan_history_id)
1397
+ if not scan_history:
1398
+ logger.error(f"Scan history with ID {scan_history_id} not found")
1399
+ return None
1400
+ else:
1401
+ scan_history = wiz_integration.create_scan_history()
1238
1402
 
1239
- if result_lower == ComplianceCheckStatus.FAIL.value.lower():
1240
- return (
1241
- label
1242
- if label_lower in ["inremediation", "in remediation", "remediation", "failed", "non-compliant"]
1243
- else None
1244
- )
1403
+ # Parse the Wiz finding data into an IntegrationFinding
1404
+ integration_finding = wiz_integration.parse_finding(wiz_finding_data, WizVulnerabilityType.VULNERABILITY)
1245
1405
 
1246
- # Not implemented or other status
1247
- return label if label_lower in ["notimplemented", "not implemented", "pending", "planned"] else None
1406
+ if not integration_finding:
1407
+ logger.warning("Failed to parse Wiz finding data into IntegrationFinding")
1408
+ return None
1248
1409
 
1410
+ # Set the asset identifier
1411
+ integration_finding.asset_identifier = asset_id
1249
1412
 
1250
- def _get_default_status_mapping(result: str) -> str:
1251
- """
1252
- Get default status mapping for a result
1413
+ # Get the asset
1414
+ asset = wiz_integration.get_asset_by_identifier(asset_id)
1415
+ if not asset:
1416
+ logger.error(f"Asset with identifier {asset_id} not found")
1417
+ return None
1253
1418
 
1254
- :param str result: Report result
1255
- :return: Default implementation status
1256
- :rtype: str
1257
- """
1258
- if result == ComplianceCheckStatus.PASS.value:
1259
- return ControlImplementationStatus.Implemented.value
1260
- elif result == ComplianceCheckStatus.FAIL.value:
1261
- return ControlImplementationStatus.InRemediation.value
1262
- else:
1263
- return ControlImplementationStatus.NotImplemented.value
1419
+ # Handle the vulnerability creation using the integration framework
1420
+ vulnerability_id = wiz_integration.handle_vulnerability(
1421
+ finding=integration_finding,
1422
+ asset=asset,
1423
+ scan_history=scan_history,
1424
+ )
1425
+
1426
+ if vulnerability_id:
1427
+ vulnerability = regscale_models.Vulnerability.get_by_id(vulnerability_id)
1428
+ logger.info(f"Successfully created vulnerability {vulnerability_id}")
1429
+ return vulnerability
1430
+ else:
1431
+ logger.warning("Failed to create vulnerability")
1432
+ return None
1433
+
1434
+ except Exception as e:
1435
+ logger.error(f"Error creating single vulnerability: {e}", exc_info=True)
1436
+ return None
@@ -3,7 +3,7 @@
3
3
  """Wiz Variables"""
4
4
 
5
5
  from regscale.core.app.utils.variables import RsVariableType, RsVariablesMeta
6
- from regscale.integrations.commercial.wizv2.constants import RECOMMENDED_WIZ_INVENTORY_TYPES
6
+ from regscale.integrations.commercial.wizv2.constants import RECOMMENDED_WIZ_INVENTORY_TYPES, DEFAULT_WIZ_HARDWARE_TYPES
7
7
 
8
8
 
9
9
  class WizVariables(metaclass=RsVariablesMeta):
@@ -32,8 +32,9 @@ class WizVariables(metaclass=RsVariablesMeta):
32
32
  useWizHardwareAssetTypes: RsVariableType(bool, False, required=False) # type: ignore
33
33
  wizHardwareAssetTypes: RsVariableType(
34
34
  list,
35
- '["SERVER_APPLICATION", "CLIENT_APPLICATION", "VIRTUAL_APPLIANCE"]',
36
- default=["SERVER_APPLICATION", "CLIENT_APPLICATION", "VIRTUAL_APPLIANCE"],
35
+ '["CONTAINER", "CONTAINER_IMAGE", "VIRTUAL_MACHINE", "VIRTUAL_MACHINE_IMAGE", "DB_SERVER", '
36
+ '"CLIENT_APPLICATION", "SERVER_APPLICATION", "VIRTUAL_APPLIANCE"]',
37
+ default=DEFAULT_WIZ_HARDWARE_TYPES,
37
38
  required=False,
38
39
  ) # type: ignore
39
40
  wizReportAge: RsVariableType(int, "14", default=14, required=False) # type: ignore