regscale-cli 6.26.0.0__py3-none-any.whl → 6.27.0.1__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 (96) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/application.py +1 -1
  3. regscale/core/app/internal/evidence.py +419 -2
  4. regscale/dev/code_gen.py +24 -20
  5. regscale/integrations/commercial/__init__.py +0 -1
  6. regscale/integrations/commercial/jira.py +367 -126
  7. regscale/integrations/commercial/qualys/__init__.py +7 -8
  8. regscale/integrations/commercial/qualys/scanner.py +8 -3
  9. regscale/integrations/commercial/synqly/assets.py +17 -0
  10. regscale/integrations/commercial/synqly/vulnerabilities.py +45 -28
  11. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  12. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  13. regscale/integrations/commercial/tenablev2/commands.py +142 -1
  14. regscale/integrations/commercial/tenablev2/scanner.py +0 -1
  15. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  16. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  17. regscale/integrations/commercial/wizv2/click.py +44 -59
  18. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  19. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  20. regscale/integrations/commercial/wizv2/compliance_report.py +10 -9
  21. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  22. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +3 -3
  23. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +1 -17
  24. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  25. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  26. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +5 -9
  27. regscale/integrations/commercial/wizv2/issue.py +1 -1
  28. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  29. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  30. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  31. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  32. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  33. regscale/integrations/commercial/wizv2/reports.py +1 -1
  34. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  35. regscale/integrations/commercial/wizv2/scanner.py +40 -100
  36. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  37. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  38. regscale/integrations/commercial/wizv2/variables.py +89 -3
  39. regscale/integrations/compliance_integration.py +0 -46
  40. regscale/integrations/control_matcher.py +22 -3
  41. regscale/integrations/due_date_handler.py +14 -8
  42. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  43. regscale/integrations/public/fedramp/fedramp_cis_crm.py +393 -340
  44. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  45. regscale/integrations/scanner_integration.py +127 -57
  46. regscale/models/integration_models/cisa_kev_data.json +132 -9
  47. regscale/models/integration_models/qualys.py +3 -4
  48. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  49. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +24 -7
  50. regscale/models/integration_models/synqly_models/synqly_model.py +8 -1
  51. regscale/models/regscale_models/control_implementation.py +1 -1
  52. regscale/models/regscale_models/issue.py +0 -1
  53. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/METADATA +1 -17
  54. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/RECORD +94 -61
  55. tests/regscale/integrations/commercial/test_jira.py +481 -91
  56. tests/regscale/integrations/commercial/test_wiz.py +96 -200
  57. tests/regscale/integrations/commercial/wizv2/__init__.py +1 -1
  58. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  59. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  60. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  61. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  62. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  63. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  64. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  65. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  66. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  67. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  68. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  69. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  70. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  71. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  72. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  73. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  74. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  75. tests/regscale/integrations/commercial/wizv2/test_issue.py +1 -1
  76. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  77. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  78. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  79. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  80. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +1 -1
  81. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +72 -29
  82. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  83. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  84. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +946 -78
  85. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +97 -202
  86. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  87. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  88. tests/regscale/integrations/public/test_fedramp.py +301 -0
  89. tests/regscale/integrations/test_control_matcher.py +83 -0
  90. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3543
  91. tests/regscale/integrations/commercial/wizv2/test_wiz_policy_compliance.py +0 -750
  92. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  93. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/LICENSE +0 -0
  94. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/WHEEL +0 -0
  95. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/entry_points.txt +0 -0
  96. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,7 @@ from regscale.core.app.utils.app_utils import get_current_datetime
15
15
  from regscale.integrations.commercial.wizv2.file_cleanup import ReportFileCleanup
16
16
  from regscale.integrations.commercial.wizv2.reports import WizReportManager
17
17
  from regscale.integrations.commercial.wizv2.variables import WizVariables
18
- from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
18
+ from regscale.integrations.commercial.wizv2.core.auth import wiz_authenticate
19
19
  from regscale.integrations.compliance_integration import ComplianceIntegration, ComplianceItem
20
20
  from regscale.integrations.control_matcher import ControlMatcher
21
21
  from regscale.models import regscale_models
@@ -793,25 +793,26 @@ class WizComplianceReportProcessor(ComplianceIntegration):
793
793
  :rtype: Optional[str]
794
794
  """
795
795
  try:
796
- # Filter for compliance reports for this specific project
797
- filter_by = {"projectId": [self.wiz_project_id], "type": ["COMPLIANCE_ASSESSMENTS"]}
796
+ # Filter for compliance reports (projectId not supported in ReportFilters, using name-based lookup)
797
+ filter_by = {"type": ["COMPLIANCE_ASSESSMENTS"]}
798
798
 
799
799
  logger.debug(f"Searching for existing compliance reports with filter: {filter_by}")
800
800
  reports = self.report_manager.list_reports(filter_by=filter_by)
801
801
 
802
802
  if not reports:
803
- logger.info("No existing compliance reports found for this project")
803
+ logger.info("No existing compliance reports found")
804
804
  return None
805
805
 
806
- # Look for reports named "Compliance Report" (the default name)
807
- compliance_reports = [report for report in reports if report.get("name", "").strip() == "Compliance Report"]
806
+ # Look for report with project-specific name
807
+ expected_name = f"Compliance Report - {self.wiz_project_id}"
808
+ matching_reports = [report for report in reports if report.get("name", "").strip() == expected_name]
808
809
 
809
- if not compliance_reports:
810
- logger.info("No compliance reports with standard name found")
810
+ if not matching_reports:
811
+ logger.info(f"No existing compliance report found with name: {expected_name}")
811
812
  return None
812
813
 
813
814
  # Return the first matching report (most recent will be used)
814
- selected_report = compliance_reports[0]
815
+ selected_report = matching_reports[0]
815
816
  report_id = selected_report.get("id")
816
817
  report_name = selected_report.get("name", "Unknown")
817
818
 
@@ -0,0 +1,133 @@
1
+ """Core Wiz integration modules including authentication, client, file operations, and constants."""
2
+
3
+ from regscale.integrations.commercial.wizv2.core.auth import (
4
+ AUTH0_URLS,
5
+ COGNITO_URLS,
6
+ generate_authentication_params,
7
+ get_token,
8
+ wiz_authenticate,
9
+ )
10
+ from regscale.integrations.commercial.wizv2.core.client import (
11
+ AsyncWizGraphQLClient,
12
+ run_async_queries,
13
+ )
14
+ from regscale.integrations.commercial.wizv2.core.file_operations import FileOperations
15
+ from regscale.integrations.commercial.wizv2.core.constants import (
16
+ ASSET_TYPE_MAPPING,
17
+ BEARER,
18
+ CHECK_INTERVAL_FOR_DOWNLOAD_REPORT,
19
+ CLOUD_CONFIG_FINDING_QUERY,
20
+ CLOUD_CONFIG_FINDINGS_FILE_PATH,
21
+ CONTENT_TYPE,
22
+ CPE_PART_TO_CATEGORY_MAPPING,
23
+ CREATE_REPORT_QUERY,
24
+ DATA_FINDING_QUERY,
25
+ DATA_FINDINGS_FILE_PATH,
26
+ DATASOURCE,
27
+ DEFAULT_WIZ_HARDWARE_TYPES,
28
+ DOWNLOAD_QUERY,
29
+ END_OF_LIFE_FILE_PATH,
30
+ END_OF_LIFE_QUERY,
31
+ EXCESSIVE_ACCESS_FILE_PATH,
32
+ EXCESSIVE_ACCESS_QUERY,
33
+ EXTERNAL_ATTACK_SURFACE_FILE_PATH,
34
+ EXTERNAL_ATTACK_SURFACE_QUERY,
35
+ FRAMEWORK_CATEGORIES,
36
+ FRAMEWORK_MAPPINGS,
37
+ FRAMEWORK_SHORTCUTS,
38
+ HOST_VULNERABILITY_FILE_PATH,
39
+ HOST_VULNERABILITY_QUERY,
40
+ INVENTORY_FILE_PATH,
41
+ INVENTORY_QUERY,
42
+ ISSUE_QUERY,
43
+ ISSUES_FILE_PATH,
44
+ MAX_RETRIES,
45
+ NETWORK_EXPOSURE_FILE_PATH,
46
+ NETWORK_EXPOSURE_QUERY,
47
+ PROVIDER,
48
+ RATE_LIMIT_MSG,
49
+ RECOMMENDED_WIZ_INVENTORY_TYPES,
50
+ REPORTS_QUERY,
51
+ RERUN_REPORT_QUERY,
52
+ RESOURCE,
53
+ SBOM_FILE_PATH,
54
+ SBOM_QUERY,
55
+ SECRET_FINDINGS_FILE_PATH,
56
+ SECRET_FINDINGS_QUERY,
57
+ SEVERITY_MAP,
58
+ TECHNOLOGIES_FILE_PATH,
59
+ VULNERABILITY_FILE_PATH,
60
+ VULNERABILITY_QUERY,
61
+ WIZ_FRAMEWORK_QUERY,
62
+ WIZ_POLICY_QUERY,
63
+ WizVulnerabilityType,
64
+ get_compliance_report_variables,
65
+ get_wiz_issue_queries,
66
+ get_wiz_vulnerability_queries,
67
+ )
68
+
69
+ __all__ = [
70
+ # Auth
71
+ "AUTH0_URLS",
72
+ "COGNITO_URLS",
73
+ "generate_authentication_params",
74
+ "get_token",
75
+ "wiz_authenticate",
76
+ # Client
77
+ "AsyncWizGraphQLClient",
78
+ "run_async_queries",
79
+ # File Operations
80
+ "FileOperations",
81
+ # Constants
82
+ "ASSET_TYPE_MAPPING",
83
+ "BEARER",
84
+ "CHECK_INTERVAL_FOR_DOWNLOAD_REPORT",
85
+ "CLOUD_CONFIG_FINDING_QUERY",
86
+ "CLOUD_CONFIG_FINDINGS_FILE_PATH",
87
+ "CONTENT_TYPE",
88
+ "CPE_PART_TO_CATEGORY_MAPPING",
89
+ "CREATE_REPORT_QUERY",
90
+ "DATA_FINDING_QUERY",
91
+ "DATA_FINDINGS_FILE_PATH",
92
+ "DATASOURCE",
93
+ "DEFAULT_WIZ_HARDWARE_TYPES",
94
+ "DOWNLOAD_QUERY",
95
+ "END_OF_LIFE_FILE_PATH",
96
+ "END_OF_LIFE_QUERY",
97
+ "EXCESSIVE_ACCESS_FILE_PATH",
98
+ "EXCESSIVE_ACCESS_QUERY",
99
+ "EXTERNAL_ATTACK_SURFACE_FILE_PATH",
100
+ "EXTERNAL_ATTACK_SURFACE_QUERY",
101
+ "FRAMEWORK_CATEGORIES",
102
+ "FRAMEWORK_MAPPINGS",
103
+ "FRAMEWORK_SHORTCUTS",
104
+ "HOST_VULNERABILITY_FILE_PATH",
105
+ "HOST_VULNERABILITY_QUERY",
106
+ "INVENTORY_FILE_PATH",
107
+ "INVENTORY_QUERY",
108
+ "ISSUE_QUERY",
109
+ "ISSUES_FILE_PATH",
110
+ "MAX_RETRIES",
111
+ "NETWORK_EXPOSURE_FILE_PATH",
112
+ "NETWORK_EXPOSURE_QUERY",
113
+ "PROVIDER",
114
+ "RATE_LIMIT_MSG",
115
+ "RECOMMENDED_WIZ_INVENTORY_TYPES",
116
+ "REPORTS_QUERY",
117
+ "RERUN_REPORT_QUERY",
118
+ "RESOURCE",
119
+ "SBOM_FILE_PATH",
120
+ "SBOM_QUERY",
121
+ "SECRET_FINDINGS_FILE_PATH",
122
+ "SECRET_FINDINGS_QUERY",
123
+ "SEVERITY_MAP",
124
+ "TECHNOLOGIES_FILE_PATH",
125
+ "VULNERABILITY_FILE_PATH",
126
+ "VULNERABILITY_QUERY",
127
+ "WIZ_FRAMEWORK_QUERY",
128
+ "WIZ_POLICY_QUERY",
129
+ "WizVulnerabilityType",
130
+ "get_compliance_report_variables",
131
+ "get_wiz_issue_queries",
132
+ "get_wiz_vulnerability_queries",
133
+ ]
@@ -4,7 +4,7 @@
4
4
 
5
5
  import asyncio
6
6
  import logging
7
- from typing import Any, Dict, List, Optional, Tuple
7
+ from typing import Any, Callable, Dict, List, Optional, Tuple
8
8
 
9
9
  import anyio
10
10
  import httpx
@@ -48,7 +48,7 @@ class AsyncWizGraphQLClient:
48
48
  self,
49
49
  query: str,
50
50
  variables: Optional[Dict[str, Any]] = None,
51
- progress_callback: Optional[callable] = None,
51
+ progress_callback: Optional[Callable] = None,
52
52
  task_name: str = "GraphQL Query",
53
53
  ) -> Dict[str, Any]:
54
54
  """
@@ -118,7 +118,7 @@ class AsyncWizGraphQLClient:
118
118
  query: str,
119
119
  variables: Dict[str, Any],
120
120
  topic_key: str,
121
- progress_callback: Optional[callable] = None,
121
+ progress_callback: Optional[Callable] = None,
122
122
  task_name: str = "Paginated Query",
123
123
  ) -> List[Dict[str, Any]]:
124
124
  """
@@ -569,7 +569,7 @@ def get_compliance_report_variables(
569
569
 
570
570
  return {
571
571
  "input": {
572
- "name": "Compliance Report",
572
+ "name": f"Compliance Report - {project_id}",
573
573
  "type": "COMPLIANCE_ASSESSMENTS",
574
574
  "compressionMethod": "GZIP",
575
575
  "runIntervalHours": 168,
@@ -632,22 +632,6 @@ REPORTS_QUERY = """
632
632
  emailTarget {
633
633
  to
634
634
  }
635
- parameters {
636
- query
637
- framework {
638
- name
639
- }
640
- subscriptions {
641
- id
642
- name
643
- type
644
- }
645
- entities {
646
- id
647
- name
648
- type
649
- }
650
- }
651
635
  lastRun {
652
636
  ...LastRunDetails
653
637
  }
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """File operations module for Wiz integration - handles caching and file I/O."""
4
+
5
+ import datetime
6
+ import json
7
+ import logging
8
+ import os
9
+ from typing import Any, Callable, Dict, List, Optional, Tuple
10
+
11
+ from regscale.core.app.utils.app_utils import check_file_path
12
+
13
+ logger = logging.getLogger("regscale")
14
+
15
+
16
+ class FileOperations:
17
+ """Handles file operations for Wiz integration including caching and data persistence."""
18
+
19
+ @staticmethod
20
+ def load_json_file(file_path: str) -> Optional[Any]:
21
+ """
22
+ Load data from a JSON file.
23
+
24
+ :param str file_path: Path to JSON file
25
+ :return: Loaded data or None if file doesn't exist or is invalid
26
+ :rtype: Optional[Any]
27
+ """
28
+ if not os.path.exists(file_path):
29
+ logger.debug(f"File does not exist: {file_path}")
30
+ return None
31
+
32
+ try:
33
+ with open(file_path, encoding="utf-8") as f:
34
+ return json.load(f)
35
+ except (OSError, json.JSONDecodeError) as e:
36
+ logger.error(f"Error reading JSON file {file_path}: {e}")
37
+ return None
38
+
39
+ @staticmethod
40
+ def save_json_file(data: Any, file_path: str, create_dir: bool = True) -> bool:
41
+ """
42
+ Save data to a JSON file.
43
+
44
+ :param Any data: Data to save
45
+ :param str file_path: Path to save file
46
+ :param bool create_dir: Whether to create parent directory if needed
47
+ :return: True if successful, False otherwise
48
+ :rtype: bool
49
+ """
50
+ try:
51
+ if create_dir:
52
+ check_file_path(os.path.dirname(file_path))
53
+
54
+ with open(file_path, "w", encoding="utf-8") as f:
55
+ json.dump(data, f)
56
+
57
+ logger.debug(f"Saved data to {file_path}")
58
+ return True
59
+
60
+ except Exception as e:
61
+ logger.warning(f"Failed to save data to {file_path}: {e}")
62
+ return False
63
+
64
+ @staticmethod
65
+ def get_file_age(file_path: str) -> Optional[datetime.timedelta]:
66
+ """
67
+ Get the age of a file as a timedelta.
68
+
69
+ :param str file_path: Path to file
70
+ :return: File age or None if file doesn't exist
71
+ :rtype: Optional[datetime.timedelta]
72
+ """
73
+ if not os.path.exists(file_path):
74
+ return None
75
+
76
+ try:
77
+ file_mod_time = datetime.datetime.fromtimestamp(os.path.getmtime(file_path))
78
+ current_time = datetime.datetime.now()
79
+ return current_time - file_mod_time
80
+ except OSError as e:
81
+ logger.warning(f"Error getting file age for {file_path}: {e}")
82
+ return None
83
+
84
+ @staticmethod
85
+ def is_cache_valid(file_path: str, max_age_hours: float = 8) -> bool:
86
+ """
87
+ Check if a cache file is valid (exists and not too old).
88
+
89
+ :param str file_path: Path to cache file
90
+ :param float max_age_hours: Maximum age in hours before cache is invalid
91
+ :return: True if cache is valid, False otherwise
92
+ :rtype: bool
93
+ """
94
+ file_age = FileOperations.get_file_age(file_path)
95
+ if file_age is None:
96
+ return False
97
+
98
+ max_age = datetime.timedelta(hours=max_age_hours)
99
+ return file_age < max_age
100
+
101
+ @staticmethod
102
+ def load_cache_or_fetch(
103
+ file_path: str,
104
+ fetch_fn: Callable[[], Any],
105
+ max_age_hours: float = 8,
106
+ save_cache: bool = True,
107
+ ) -> Any:
108
+ """
109
+ Load data from cache if valid, otherwise fetch and optionally cache.
110
+
111
+ :param str file_path: Path to cache file
112
+ :param Callable fetch_fn: Function to call to fetch fresh data
113
+ :param float max_age_hours: Maximum cache age in hours
114
+ :param bool save_cache: Whether to save fetched data to cache
115
+ :return: Data from cache or freshly fetched
116
+ :rtype: Any
117
+ """
118
+ # Try to load from cache if valid
119
+ if FileOperations.is_cache_valid(file_path, max_age_hours):
120
+ logger.info(f"Using cached data from {file_path} (newer than {max_age_hours} hours)")
121
+ cached_data = FileOperations.load_json_file(file_path)
122
+ if cached_data is not None:
123
+ return cached_data
124
+
125
+ # Cache invalid or doesn't exist - fetch fresh data
126
+ file_age = FileOperations.get_file_age(file_path)
127
+ if file_age:
128
+ logger.info(
129
+ f"Cache file {file_path} is {file_age.total_seconds() / 3600:.1f} hours old - fetching new data"
130
+ )
131
+ else:
132
+ logger.info(f"Cache file {file_path} does not exist - fetching new data")
133
+
134
+ data = fetch_fn()
135
+
136
+ # Save to cache if requested
137
+ if save_cache:
138
+ FileOperations.save_json_file(data, file_path)
139
+
140
+ return data
141
+
142
+ @staticmethod
143
+ def search_json_files(
144
+ identifier: str,
145
+ file_paths: List[str],
146
+ match_fn: Callable[[Dict, str], bool],
147
+ ) -> Tuple[Optional[Dict], Optional[str]]:
148
+ """
149
+ Search for an item across multiple JSON files.
150
+
151
+ :param str identifier: Identifier to search for
152
+ :param List[str] file_paths: List of file paths to search
153
+ :param Callable match_fn: Function to determine if an item matches (takes item and identifier)
154
+ :return: Tuple of (found_item, source_file) or (None, None)
155
+ :rtype: Tuple[Optional[Dict], Optional[str]]
156
+ """
157
+ for file_path in file_paths:
158
+ if not os.path.exists(file_path):
159
+ continue
160
+
161
+ try:
162
+ result = FileOperations.search_single_json_file(identifier, file_path, match_fn)
163
+ if result:
164
+ return result, file_path
165
+ except Exception as e:
166
+ logger.debug(f"Error searching {file_path}: {e}")
167
+ continue
168
+
169
+ return None, None
170
+
171
+ @staticmethod
172
+ def search_single_json_file(
173
+ identifier: str,
174
+ file_path: str,
175
+ match_fn: Callable[[Dict, str], bool],
176
+ ) -> Optional[Dict]:
177
+ """
178
+ Search for an item in a single JSON file.
179
+
180
+ :param str identifier: Identifier to search for
181
+ :param str file_path: Path to JSON file
182
+ :param Callable match_fn: Function to determine if an item matches
183
+ :return: Matched item or None
184
+ :rtype: Optional[Dict]
185
+ """
186
+ logger.debug(f"Searching for {identifier} in {file_path}")
187
+
188
+ data = FileOperations.load_json_file(file_path)
189
+ if not isinstance(data, list):
190
+ return None
191
+
192
+ # Use generator for memory efficiency
193
+ return next((item for item in data if match_fn(item, identifier)), None)
194
+
195
+ @staticmethod
196
+ def load_cached_findings(
197
+ query_configs: List[Dict[str, Any]],
198
+ progress_callback: Optional[Callable] = None,
199
+ ) -> List[Tuple[str, List[Dict], Optional[Exception]]]:
200
+ """
201
+ Load cached findings from multiple files.
202
+
203
+ :param List[Dict[str, Any]] query_configs: Query configurations with file paths
204
+ :param Optional[Callable] progress_callback: Optional progress callback
205
+ :return: List of (query_type, nodes, error) tuples
206
+ :rtype: List[Tuple[str, List[Dict], Optional[Exception]]]
207
+ """
208
+ results = []
209
+
210
+ for config in query_configs:
211
+ query_type = config["type"].value
212
+ file_path = config.get("file_path")
213
+
214
+ if progress_callback:
215
+ progress_callback(query_type, "loading")
216
+
217
+ try:
218
+ if file_path and os.path.exists(file_path):
219
+ nodes = FileOperations.load_json_file(file_path)
220
+ if nodes is not None:
221
+ logger.info(f"Loaded {len(nodes)} cached {query_type} findings from {file_path}")
222
+ results.append((query_type, nodes, None))
223
+ else:
224
+ logger.warning(f"Failed to load cached data for {query_type}")
225
+ results.append((query_type, [], Exception(f"Failed to load {file_path}")))
226
+ else:
227
+ logger.warning(f"No cached data found for {query_type} at {file_path}")
228
+ results.append((query_type, [], None))
229
+
230
+ except Exception as e:
231
+ logger.error(f"Error loading cached data for {query_type}: {e}")
232
+ results.append((query_type, [], e))
233
+
234
+ if progress_callback:
235
+ progress_callback(query_type, "loaded")
236
+
237
+ return results
@@ -0,0 +1,11 @@
1
+ """Fetchers for Wiz integration - handles data retrieval and caching."""
2
+
3
+ from regscale.integrations.commercial.wizv2.fetchers.policy_assessment import (
4
+ PolicyAssessmentFetcher,
5
+ WizDataCache,
6
+ )
7
+
8
+ __all__ = [
9
+ "PolicyAssessmentFetcher",
10
+ "WizDataCache",
11
+ ]
@@ -8,8 +8,8 @@ import os
8
8
  from datetime import datetime
9
9
  from typing import Dict, List, Optional, Any, Callable
10
10
 
11
- from regscale.integrations.commercial.wizv2.async_client import run_async_queries
12
- from regscale.integrations.commercial.wizv2.constants import WizVulnerabilityType, WIZ_POLICY_QUERY
11
+ from regscale.integrations.commercial.wizv2.core.client import run_async_queries
12
+ from regscale.integrations.commercial.wizv2.core.constants import WizVulnerabilityType, WIZ_POLICY_QUERY
13
13
 
14
14
  logger = logging.getLogger("regscale")
15
15
 
@@ -127,14 +127,10 @@ class WizApiClient:
127
127
  "Content-Type": "application/json",
128
128
  }
129
129
 
130
- def fetch_policy_assessments_async(
131
- self, wiz_project_id: str, progress_callback: Optional[Callable] = None
132
- ) -> List[Dict[str, Any]]:
130
+ def fetch_policy_assessments_async(self) -> List[Dict[str, Any]]:
133
131
  """
134
132
  Fetch policy assessments using async client.
135
133
 
136
- :param wiz_project_id: Wiz project ID
137
- :param progress_callback: Optional progress callback
138
134
  :return: List of policy assessment nodes
139
135
  """
140
136
  try:
@@ -204,7 +200,7 @@ class WizApiClient:
204
200
  logger.debug(f"Filter variant {filter_variant} failed: {e}")
205
201
  continue
206
202
 
207
- raise Exception(f"All filter variants failed. Last error: {last_error}")
203
+ raise RuntimeError(f"All filter variants failed. Last error: {last_error}")
208
204
 
209
205
  def _create_requests_session(self):
210
206
  """
@@ -350,7 +346,7 @@ class PolicyAssessmentFetcher:
350
346
 
351
347
  :return: List of policy assessment nodes
352
348
  """
353
- return self.api_client.fetch_policy_assessments_async(self.wiz_project_id)
349
+ return self.api_client.fetch_policy_assessments_async()
354
350
 
355
351
  def _fetch_with_requests(self) -> List[Dict[str, Any]]:
356
352
  """
@@ -9,7 +9,7 @@ from regscale.core.app.utils.parser_utils import safe_datetime_str
9
9
  from regscale.integrations.scanner_integration import IntegrationFinding
10
10
  from regscale.models import Issue
11
11
  from regscale.utils.dict_utils import get_value
12
- from .constants import (
12
+ from .core.constants import (
13
13
  get_wiz_issue_queries,
14
14
  WizVulnerabilityType,
15
15
  )
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Parsers module for Wiz integration - re-exports from main.py for clean imports."""
4
+
5
+ # Import all parser functions from the main parsers module
6
+ from regscale.integrations.commercial.wizv2.parsers.main import (
7
+ collect_components_to_create,
8
+ fetch_wiz_data,
9
+ get_disk_storage,
10
+ get_ip_address,
11
+ get_latest_version,
12
+ get_network_info,
13
+ get_product_ids,
14
+ get_software_name_from_cpe,
15
+ handle_container_image_version,
16
+ handle_provider,
17
+ handle_software_version,
18
+ pull_resource_info_from_props,
19
+ )
20
+
21
+ __all__ = [
22
+ "collect_components_to_create",
23
+ "fetch_wiz_data",
24
+ "get_disk_storage",
25
+ "get_ip_address",
26
+ "get_latest_version",
27
+ "get_network_info",
28
+ "get_product_ids",
29
+ "get_software_name_from_cpe",
30
+ "handle_container_image_version",
31
+ "handle_provider",
32
+ "handle_software_version",
33
+ "pull_resource_info_from_props",
34
+ ]
@@ -3,7 +3,7 @@ import logging
3
3
  from typing import Dict, Optional, Tuple, Union, List, Any
4
4
 
5
5
  from regscale.integrations.commercial.cpe import extract_product_name_and_version
6
- from regscale.integrations.commercial.wizv2.constants import CONTENT_TYPE
6
+ from regscale.integrations.commercial.wizv2.core.constants import CONTENT_TYPE
7
7
  from regscale.integrations.commercial.wizv2.variables import WizVariables
8
8
  from regscale.models import regscale_models
9
9
  from regscale.utils import PaginatedGraphQLClient
@@ -0,0 +1,11 @@
1
+ """Processors for Wiz integration - handles business logic and data transformation."""
2
+
3
+ from regscale.integrations.commercial.wizv2.processors.finding import (
4
+ FindingConsolidator,
5
+ FindingToIssueProcessor,
6
+ )
7
+
8
+ __all__ = [
9
+ "FindingConsolidator",
10
+ "FindingToIssueProcessor",
11
+ ]
@@ -7,7 +7,7 @@ from typing import Dict, List, Optional, Iterator, Any, Set, Union
7
7
  from collections import defaultdict
8
8
 
9
9
  from regscale.integrations.scanner_integration import IntegrationFinding
10
- from regscale.integrations.commercial.wizv2.policy_compliance_helpers import AssetConsolidator
10
+ from regscale.integrations.commercial.wizv2.compliance.helpers import AssetConsolidator
11
11
  from regscale.models import regscale_models
12
12
 
13
13
  logger = logging.getLogger("regscale")
@@ -8,7 +8,7 @@ from typing import Dict, Any, Optional
8
8
 
9
9
  import requests
10
10
 
11
- from regscale.integrations.commercial.wizv2.constants import (
11
+ from regscale.integrations.commercial.wizv2.core.constants import (
12
12
  CREATE_REPORT_QUERY,
13
13
  REPORTS_QUERY,
14
14
  DOWNLOAD_QUERY,
@@ -6,7 +6,7 @@ from typing import List, Dict, Optional
6
6
  from regscale.core.app.utils.app_utils import error_and_exit
7
7
  from regscale.integrations.commercial.wizv2.WizDataMixin import WizMixin
8
8
  from regscale.models.regscale_models.sbom import Sbom
9
- from regscale.integrations.commercial.wizv2.constants import SBOM_QUERY, SBOM_FILE_PATH
9
+ from regscale.integrations.commercial.wizv2.core.constants import SBOM_QUERY, SBOM_FILE_PATH
10
10
  from regscale.utils import get_value
11
11
 
12
12
  logger = logging.getLogger(__name__)