regscale-cli 6.21.2.0__py3-none-any.whl → 6.21.2.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.

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.21.2.0" # fallback version
36
+ return "6.21.2.1" # fallback version
37
37
 
38
38
 
39
39
  __version__ = get_version_from_pyproject()
@@ -260,6 +260,7 @@ class Application(metaclass=Singleton):
260
260
  "tenableMinimumSeverityFilter": "low",
261
261
  "token": DEFAULT_POPULATED,
262
262
  "userId": "enter RegScale user id here",
263
+ "useMilestones": False,
263
264
  "otx": "enter AlienVault API key here",
264
265
  "wizAccessToken": DEFAULT_POPULATED,
265
266
  "wizAuthUrl": "https://auth.wiz.io/oauth/token",
@@ -39,6 +39,7 @@ from regscale.integrations.scanner_integration import (
39
39
  IntegrationFinding,
40
40
  issue_due_date,
41
41
  )
42
+ from regscale.integrations.variables import ScannerVariables
42
43
  from regscale.models import regscale_models
43
44
 
44
45
  logger = logging.getLogger("regscale")
@@ -193,6 +194,8 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
193
194
  type = ScannerIntegrationType.CONTROL_TEST
194
195
  # Use wizId field for asset identification (matches other Wiz integrations)
195
196
  asset_identifier_field = "wizId"
197
+ issue_identifier_field = "wizId"
198
+
196
199
  # Do not create assets - they come from separate inventory import
197
200
  options_map_assets_to_components: bool = False
198
201
  # Do not create vulnerabilities from compliance policy results
@@ -906,7 +909,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
906
909
 
907
910
  asset = IntegrationAsset(
908
911
  name=compliance_item.resource_name,
909
- identifier=f"{compliance_item.resource_name} ({compliance_item.resource_id})",
912
+ identifier=compliance_item.resource_name, # Use name only without UUID
910
913
  external_id=compliance_item.resource_id,
911
914
  other_tracking_number=compliance_item.resource_id, # For deduplication
912
915
  asset_type=asset_type,
@@ -1239,7 +1242,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
1239
1242
  filtered_nodes = self._filter_nodes_to_framework(nodes)
1240
1243
  progress.update(
1241
1244
  task,
1242
- description=f"[green]Completed Wiz policy assessments: {len(filtered_nodes)} nodes",
1245
+ description=f"[green]Completed Wiz policy assessments: {len(filtered_nodes)} nodes",
1243
1246
  completed=1,
1244
1247
  total=1,
1245
1248
  )
@@ -1816,19 +1819,19 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
1816
1819
  self._sync_assets()
1817
1820
 
1818
1821
  # Step 2: CRITICAL - Pre-populate control implementation cache BEFORE creating assessments
1819
- logger.info("🔧 Pre-populating control implementation cache for issue processing...")
1822
+ logger.info("Pre-populating control implementation cache for issue processing...")
1820
1823
  self._populate_control_implementation_cache()
1821
1824
 
1822
1825
  # Step 3: Create control assessments BEFORE issues (ensures assessmentId is available)
1823
- logger.info("🔧 Creating control assessments BEFORE issue processing...")
1826
+ logger.info("Creating control assessments BEFORE issue processing...")
1824
1827
  self._sync_control_assessments()
1825
1828
 
1826
1829
  # Step 3.5: CRITICAL - Refresh assessment cache after assessments are created
1827
- logger.info("🔧 Refreshing assessment cache with newly created assessments...")
1830
+ logger.info("Refreshing assessment cache with newly created assessments...")
1828
1831
  self._refresh_assessment_cache_after_creation()
1829
1832
 
1830
1833
  # Step 4: NOW process issues with controlId and assessmentId properly set
1831
- logger.info("🔧 Processing issues with control and assessment IDs available...")
1834
+ logger.info("Processing issues with control and assessment IDs available...")
1832
1835
  self._sync_issues()
1833
1836
 
1834
1837
  self._finalize_scan_history(scan_history)
@@ -1868,7 +1871,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
1868
1871
 
1869
1872
  # Step 3: Sync using the overridden method (which ensures proper ordering)
1870
1873
  logger.info(
1871
- f"🔧 Sync parameters: create_issues={self.create_issues}, update_control_status={self.update_control_status}"
1874
+ f"Sync parameters: create_issues={self.create_issues}, update_control_status={self.update_control_status}"
1872
1875
  )
1873
1876
 
1874
1877
  self.sync_compliance()
@@ -1932,7 +1935,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
1932
1935
  # CRITICAL FIX: If assessment_id is set, prepare the finding for assessment parenting
1933
1936
  if hasattr(finding, "assessment_id") and finding.assessment_id:
1934
1937
  assessment_id = finding.assessment_id
1935
- logger.debug(f"🔄 PRE-SETTING ASSESSMENT PARENT: assessmentId={assessment_id}")
1938
+ logger.debug(f"PRE-SETTING ASSESSMENT PARENT: assessmentId={assessment_id}")
1936
1939
 
1937
1940
  # Add parent override fields to the finding for the ScannerIntegration to use
1938
1941
  finding._override_parent_id = assessment_id
@@ -2031,7 +2034,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2031
2034
  if hasattr(finding, "_override_parent_id") and hasattr(finding, "_override_parent_module"):
2032
2035
  parent_id = finding._override_parent_id
2033
2036
  parent_module = finding._override_parent_module
2034
- logger.debug(f"🔄 USING OVERRIDE PARENT: {parent_module} #{parent_id}")
2037
+ logger.debug(f"USING OVERRIDE PARENT: {parent_module} #{parent_id}")
2035
2038
  else:
2036
2039
  parent_id = self.plan_id
2037
2040
  parent_module = self.parent_module
@@ -2084,7 +2087,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2084
2087
 
2085
2088
  # CRITICAL: Set assessmentId (this is the key fix)
2086
2089
  issue.assessmentId = finding.assessment_id
2087
- logger.debug(f"SETTING assessmentId = {finding.assessment_id} with parent = {parent_module} #{parent_id}")
2090
+ logger.debug(f"SETTING assessmentId = {finding.assessment_id} with parent = {parent_module} #{parent_id}")
2088
2091
 
2089
2092
  control_id = self.get_control_implementation_id_for_cci(finding.cci_ref) if finding.cci_ref else None
2090
2093
  issue.controlId = control_id
@@ -2117,16 +2120,30 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2117
2120
  issue = self.lookup_kev_and_update_issue(cve=finding.cve, issue=issue, cisa_kevs=self._kev_data)
2118
2121
 
2119
2122
  if existing_issue:
2120
- logger.debug(f"💾 Saving existing issue {issue.id} with assessmentId={issue.assessmentId}")
2123
+ logger.debug(f"Saving existing issue {issue.id} with assessmentId={issue.assessmentId}")
2121
2124
  issue.save(bulk=True)
2122
2125
  else:
2123
- logger.info(f"💾 Creating new issue with assessmentId={issue.assessmentId}")
2126
+ logger.info(f"Creating new issue with assessmentId={issue.assessmentId}")
2124
2127
  issue = issue.create_or_update(
2125
2128
  bulk_update=True, defaults={"otherIdentifier": self._get_other_identifier(finding, is_poam)}
2126
2129
  )
2127
- self.extra_data_to_properties(finding, issue.id)
2130
+ if issue and issue.id:
2131
+ logger.debug(f"Issue created with ID: {issue.id}")
2132
+ self.extra_data_to_properties(finding, issue.id)
2133
+ else:
2134
+ logger.error(f" Issue creation failed - no ID returned for finding {finding.external_id}")
2135
+ return None
2136
+
2137
+ # Only create milestones if issue has an ID
2138
+ if issue and issue.id:
2139
+ # Check if existing issue needs initial milestone creation
2140
+ if existing_issue and ScannerVariables.useMilestones:
2141
+ self._ensure_issue_has_milestone(issue, finding)
2142
+
2143
+ self._handle_property_and_milestone_creation(issue, finding, existing_issue)
2144
+ else:
2145
+ logger.debug("Skipping milestone creation - issue has no ID")
2128
2146
 
2129
- self._handle_property_and_milestone_creation(issue, finding, existing_issue)
2130
2147
  return issue
2131
2148
 
2132
2149
  def _populate_compliance_fields_on_finding(self, finding: IntegrationFinding) -> None:
@@ -2163,6 +2180,59 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2163
2180
  except Exception:
2164
2181
  pass
2165
2182
 
2183
+ def _ensure_issue_has_milestone(self, issue: regscale_models.Issue, finding: IntegrationFinding) -> None:
2184
+ """
2185
+ Ensure that an existing issue has at least one milestone.
2186
+
2187
+ This method checks if an existing issue has any milestones, and if not,
2188
+ creates an initial "Issue created" milestone. This handles cases where
2189
+ issues were created before milestone tracking was enabled, or were
2190
+ created through other means without milestones.
2191
+
2192
+ :param issue: The existing issue to check for milestones
2193
+ :param finding: The finding data
2194
+ :return: None
2195
+ """
2196
+ try:
2197
+ # Check if the issue already has milestones
2198
+ # We need to make a direct API call because the Milestone model's endpoint configuration
2199
+ # doesn't include the module parameter that the API expects
2200
+ from regscale.models.regscale_models.milestone import Milestone
2201
+
2202
+ try:
2203
+ existing_milestones = Milestone.get_all_by_parent(parent_id=issue.id, parent_module="issues")
2204
+ logger.debug(f"Fetched {len(existing_milestones)} existing milestones for issue {issue.id}")
2205
+ except Exception as api_error:
2206
+ # If the API call fails, log it and assume no milestones exist
2207
+ logger.debug(f"Could not fetch existing milestones for issue {issue.id}: {api_error}")
2208
+ existing_milestones = []
2209
+
2210
+ if not existing_milestones:
2211
+ # Create an initial milestone for the existing issue
2212
+ logger.debug(f"Creating initial milestone for existing issue {issue.id} that had no milestones")
2213
+
2214
+ # Use the issue's dateCreated if available, otherwise use current date
2215
+ if hasattr(issue, "dateCreated") and issue.dateCreated:
2216
+ # Convert to string if it's a datetime object (e.g., in tests)
2217
+ if hasattr(issue.dateCreated, "isoformat"):
2218
+ milestone_date = issue.dateCreated.isoformat()
2219
+ else:
2220
+ milestone_date = issue.dateCreated
2221
+ else:
2222
+ milestone_date = get_current_datetime()
2223
+
2224
+ regscale_models.Milestone(
2225
+ title=f"Issue created by {self.title}",
2226
+ milestoneDate=milestone_date,
2227
+ responsiblePersonId=self.assessor_id,
2228
+ parentID=issue.id,
2229
+ parentModule=regscale_models.Issue.get_module_slug(),
2230
+ ).create()
2231
+
2232
+ logger.debug(f"Created initial milestone for existing issue {issue.id}")
2233
+ except Exception as e:
2234
+ logger.warning(f"Could not check/create milestone for issue {issue.id}: {e}")
2235
+
2166
2236
  def _enhance_issue_with_compliance_fields(self, issue: regscale_models.Issue, finding: IntegrationFinding) -> None:
2167
2237
  """
2168
2238
  Enhance an issue with compliance-specific fields (controlId and assessmentId).
@@ -2231,7 +2301,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2231
2301
  try:
2232
2302
  from regscale.models import regscale_models
2233
2303
 
2234
- logger.info("🔍 Pre-populating control implementation cache for issue processing...")
2304
+ logger.info("Pre-populating control implementation cache for issue processing...")
2235
2305
 
2236
2306
  # Get all control implementations for this plan
2237
2307
  implementations = regscale_models.ControlImplementation.get_all_by_parent(
@@ -2285,7 +2355,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2285
2355
  except Exception:
2286
2356
  continue
2287
2357
 
2288
- logger.info("Control implementation cache populated:")
2358
+ logger.info("Control implementation cache populated:")
2289
2359
  logger.info(f" - {controls_mapped} control ID mappings")
2290
2360
  logger.info(f" - {assessments_mapped} assessment mappings")
2291
2361
 
@@ -2307,7 +2377,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2307
2377
  from regscale.models import regscale_models
2308
2378
  from datetime import datetime
2309
2379
 
2310
- logger.info("🔄 Refreshing assessment cache with newly created assessments...")
2380
+ logger.info("Refreshing assessment cache with newly created assessments...")
2311
2381
 
2312
2382
  refreshed_count = 0
2313
2383
  today = datetime.now().date()
@@ -2361,7 +2431,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2361
2431
  except Exception:
2362
2432
  continue
2363
2433
 
2364
- logger.info(f"Assessment cache refreshed: {refreshed_count} assessments updated")
2434
+ logger.info(f"Assessment cache refreshed: {refreshed_count} assessments updated")
2365
2435
 
2366
2436
  except Exception as e:
2367
2437
  logger.error(f"Error refreshing assessment cache: {e}")
@@ -2408,7 +2478,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2408
2478
  impl_control_id = self._normalize_control_id_string(security_control.controlId)
2409
2479
 
2410
2480
  if impl_control_id == control_id:
2411
- logger.info(f"Found control implementation {impl.id} for control {control_id}")
2481
+ logger.info(f"Found control implementation {impl.id} for control {control_id}")
2412
2482
  # Cache it for future lookups
2413
2483
  if not hasattr(self, "_impl_id_by_control"):
2414
2484
  self._impl_id_by_control = {}
@@ -2418,7 +2488,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2418
2488
  continue
2419
2489
 
2420
2490
  logger.warning(
2421
- f"⚠️ No control implementation found for control {control_id} among {len(implementations)} implementations"
2491
+ f"No control implementation found for control {control_id} among {len(implementations)} implementations"
2422
2492
  )
2423
2493
  return None
2424
2494
  except Exception as e:
@@ -2495,9 +2565,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2495
2565
  # Sort by ID (highest/newest first) if multiple today
2496
2566
  today_assessments.sort(key=lambda a: a.id if hasattr(a, "id") else 0, reverse=True)
2497
2567
  assessment = today_assessments[0]
2498
- logger.info(
2499
- f"✓ Found today's assessment {assessment.id} for control implementation {implementation_id}"
2500
- )
2568
+ logger.info(f"Found today's assessment {assessment.id} for control implementation {implementation_id}")
2501
2569
  # Cache it for future lookups
2502
2570
  if not hasattr(self, "_assessment_by_impl_today"):
2503
2571
  self._assessment_by_impl_today = {}
@@ -2512,14 +2580,14 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2512
2580
  reverse=True,
2513
2581
  )
2514
2582
  assessment = recent_assessments[0][0]
2515
- logger.info(f"Found recent assessment {assessment.id} for control implementation {implementation_id}")
2583
+ logger.info(f"Found recent assessment {assessment.id} for control implementation {implementation_id}")
2516
2584
  # Cache it even if not today's
2517
2585
  if not hasattr(self, "_assessment_by_impl_today"):
2518
2586
  self._assessment_by_impl_today = {}
2519
2587
  self._assessment_by_impl_today[implementation_id] = assessment
2520
2588
  return assessment.id
2521
2589
 
2522
- logger.warning(f"⚠️ No usable assessments found for control implementation {implementation_id}")
2590
+ logger.warning(f"No usable assessments found for control implementation {implementation_id}")
2523
2591
  return None
2524
2592
  except Exception as e:
2525
2593
  logger.error(f"Error finding assessment for control implementation {implementation_id}: {e}")
@@ -2558,7 +2626,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2558
2626
  with existing assets in RegScale. This ensures we don't create assessments for
2559
2627
  controls that have no assets in our boundary.
2560
2628
  """
2561
- logger.info("🎯 Starting control assessment processing for Wiz compliance integration")
2629
+ logger.info("Starting control assessment processing for Wiz compliance integration")
2562
2630
 
2563
2631
  # Ensure existing records cache is loaded
2564
2632
  self._load_existing_records_cache()
@@ -2615,8 +2683,8 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2615
2683
  validated_passing_controls[control_id] = self.passing_controls[control_id]
2616
2684
 
2617
2685
  if not validated_controls_with_assets:
2618
- logger.warning(" No controls have assets in RegScale boundary - no control assessments will be created")
2619
- logger.info("📊 SUMMARY: 0 control assessments created (no assets exist in RegScale)")
2686
+ logger.warning(" No controls have assets in RegScale boundary - no control assessments will be created")
2687
+ logger.info("SUMMARY: 0 control assessments created (no assets exist in RegScale)")
2620
2688
  return
2621
2689
 
2622
2690
  assessments_created = 0
@@ -2638,15 +2706,15 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2638
2706
 
2639
2707
  if assessments_created > 0:
2640
2708
  logger.info(
2641
- f"Created {assessments_created} control assessments: {passing_assessments} passing, {failing_assessments} failing"
2709
+ f"Created {assessments_created} control assessments: {passing_assessments} passing, {failing_assessments} failing"
2642
2710
  )
2643
2711
  else:
2644
2712
  logger.warning(
2645
- f"⚠️ No control assessments were actually created (0 assessments) despite finding {len(validated_controls_with_assets)} controls with assets"
2713
+ f"No control assessments were actually created (0 assessments) despite finding {len(validated_controls_with_assets)} controls with assets"
2646
2714
  )
2647
2715
 
2648
2716
  logger.info(
2649
- f"📊 CONTROL ASSESSMENT SUMMARY: {assessments_created} assessments created for {len(validated_controls_with_assets)} validated controls"
2717
+ f"CONTROL ASSESSMENT SUMMARY: {assessments_created} assessments created for {len(validated_controls_with_assets)} validated controls"
2650
2718
  )
2651
2719
 
2652
2720
  def _sync_assessment_cache_from_base_class(self) -> None:
@@ -2665,10 +2733,10 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2665
2733
  self._control_cache.set_assessment(impl_id, assessment)
2666
2734
  synced_count += 1
2667
2735
 
2668
- logger.info(f"Synced {synced_count} assessments from base class cache to control cache")
2736
+ logger.info(f"Synced {synced_count} assessments from base class cache to control cache")
2669
2737
 
2670
2738
  except Exception as e:
2671
- logger.warning(f"⚠️ Failed to sync assessment cache: {e}")
2739
+ logger.warning(f"Failed to sync assessment cache: {e}")
2672
2740
 
2673
2741
  def _get_validated_control_compliance_items(self, control_id: str) -> List[ComplianceItem]:
2674
2742
  """
@@ -2861,7 +2929,7 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2861
2929
  self._load_existing_records_cache()
2862
2930
 
2863
2931
  # CRITICAL: Pre-populate control implementation cache before any processing
2864
- logger.info("🎯 Pre-populating control implementation cache for reliable issue linking...")
2932
+ logger.info("Pre-populating control implementation cache for reliable issue linking...")
2865
2933
  self._populate_control_implementation_cache()
2866
2934
 
2867
2935
  # Call parent's compliance data processing (assessments, etc.) but skip issue creation
@@ -2891,10 +2959,10 @@ class WizPolicyComplianceIntegration(ComplianceIntegration):
2891
2959
  )
2892
2960
 
2893
2961
  logger.info(
2894
- f"📊 SUMMARY: Processed {issues_processed} policy violations resulting in {actual_issues} consolidated issues for failed controls for assets in RegScale"
2962
+ f"SUMMARY: Processed {issues_processed} policy violations resulting in {actual_issues} consolidated issues for failed controls for assets in RegScale"
2895
2963
  )
2896
2964
  else:
2897
- logger.info("📊 SUMMARY: No issues processed - no failed controls with existing assets")
2965
+ logger.info("SUMMARY: No issues processed - no failed controls with existing assets")
2898
2966
 
2899
2967
  except Exception as e:
2900
2968
  error_and_exit(f"Error during Wiz compliance sync: {e}")
@@ -3017,7 +3085,7 @@ def list_available_frameworks() -> str:
3017
3085
  output.append("=" * 50)
3018
3086
 
3019
3087
  # Show shorthand mappings first
3020
- output.append("\n📋 Quick Shortcuts:")
3088
+ output.append("\nQuick Shortcuts:")
3021
3089
  output.append("-" * 20)
3022
3090
  shortcut_items = sorted(FRAMEWORK_SHORTCUTS.items())
3023
3091
  for shorthand, framework_id in shortcut_items[:10]: # Show first 10
@@ -201,7 +201,7 @@ class ComplianceIntegration(ScannerIntegration, ABC):
201
201
  self._load_existing_assessments()
202
202
 
203
203
  self._cache_loaded = True
204
- logger.info("🗄️ Loaded existing records cache to prevent duplicates:")
204
+ logger.info("Loaded existing records cache to prevent duplicates:")
205
205
  logger.info(f" - Assets: {len(self._existing_assets_cache)}")
206
206
  logger.info(f" - Issues: {len(self._existing_issues_cache)}")
207
207
  logger.info(f" - Assessments: {len(self._existing_assessments_cache)}")
@@ -253,7 +253,7 @@ class ComplianceIntegration(ScannerIntegration, ABC):
253
253
  parent_id=self.plan_id, parent_module=self.parent_module
254
254
  )
255
255
  all_issues.update(plan_issues)
256
- logger.debug(f"🔍 Found {len(plan_issues)} issues directly under plan {self.plan_id}")
256
+ logger.debug(f"Found {len(plan_issues)} issues directly under plan {self.plan_id}")
257
257
 
258
258
  # Method 2: Get issues associated with control implementations (matches scanner integration logic)
259
259
  try:
@@ -274,11 +274,11 @@ class ComplianceIntegration(ScannerIntegration, ABC):
274
274
  except Exception as e:
275
275
  logger.debug(f"Could not load issue {issue_id}: {e}")
276
276
 
277
- logger.debug(f"🔍 Found {impl_issues_count} additional issues via control implementations")
277
+ logger.debug(f"Found {impl_issues_count} additional issues via control implementations")
278
278
  except Exception as e:
279
279
  logger.debug(f"Could not load issues by control implementation: {e}")
280
280
 
281
- logger.debug(f"🔍 Total unique issues found: {len(all_issues)} for plan {self.plan_id}")
281
+ logger.debug(f"Total unique issues found: {len(all_issues)} for plan {self.plan_id}")
282
282
 
283
283
  wiz_issues = 0
284
284
  for issue in all_issues:
@@ -287,11 +287,11 @@ class ComplianceIntegration(ScannerIntegration, ABC):
287
287
  self._existing_issues_cache[issue.externalId] = issue
288
288
  if "wiz-policy" in issue.externalId.lower():
289
289
  wiz_issues += 1
290
- logger.debug(f"📋 Cached Wiz issue: {issue.id} -> external_id: {issue.externalId}")
290
+ logger.debug(f"Cached Wiz issue: {issue.id} -> external_id: {issue.externalId}")
291
291
  if hasattr(issue, "otherIdentifier") and issue.otherIdentifier:
292
292
  self._existing_issues_cache[issue.otherIdentifier] = issue
293
293
 
294
- logger.debug(f"🔍 Cached {wiz_issues} Wiz policy issues out of {len(all_issues)} total issues")
294
+ logger.debug(f"Cached {wiz_issues} Wiz policy issues out of {len(all_issues)} total issues")
295
295
 
296
296
  except Exception as e:
297
297
  logger.debug(f"Error loading existing issues: {e}")
@@ -1,9 +1,55 @@
1
1
  {
2
2
  "title": "CISA Catalog of Known Exploited Vulnerabilities",
3
- "catalogVersion": "2025.08.21",
4
- "dateReleased": "2025-08-21T17:02:19.8046Z",
5
- "count": 1401,
3
+ "catalogVersion": "2025.08.25",
4
+ "dateReleased": "2025-08-25T17:04:19.9796Z",
5
+ "count": 1404,
6
6
  "vulnerabilities": [
7
+ {
8
+ "cveID": "CVE-2025-48384",
9
+ "vendorProject": "Git",
10
+ "product": "Git",
11
+ "vulnerabilityName": "Git Link Following Vulnerability",
12
+ "dateAdded": "2025-08-25",
13
+ "shortDescription": "Git contains a link following vulnerability that stems from Git\u2019s inconsistent handling of carriage return characters in configuration files.",
14
+ "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
15
+ "dueDate": "2025-09-15",
16
+ "knownRansomwareCampaignUse": "Unknown",
17
+ "notes": "This vulnerability affects a common open-source component, third-party library, or a protocol used by different products. For more information, please see: https:\/\/github.com\/git\/git\/security\/advisories\/GHSA-vwqx-4fm8-6qc9 ; https:\/\/access.redhat.com\/errata\/RHSA-2025:13933 ; https:\/\/alas.aws.amazon.com\/AL2\/ALAS2-2025-2941.html ; https:\/\/linux.oracle.com\/errata\/ELSA-2025-11534.html ; https:\/\/msrc.microsoft.com\/update-guide\/vulnerability\/CVE-2025-48384 ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2025-48384",
18
+ "cwes": [
19
+ "CWE-59",
20
+ "CWE-436"
21
+ ]
22
+ },
23
+ {
24
+ "cveID": "CVE-2024-8068",
25
+ "vendorProject": "Citrix",
26
+ "product": "Session Recording",
27
+ "vulnerabilityName": "Citrix Session Recording Improper Privilege Management Vulnerability",
28
+ "dateAdded": "2025-08-25",
29
+ "shortDescription": "Citrix Session Recording contains an improper privilege management vulnerability that could allow for privilege escalation to NetworkService Account access. An attacker must be an authenticated user in the same Windows Active Directory domain as the session recording server domain.",
30
+ "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
31
+ "dueDate": "2025-09-15",
32
+ "knownRansomwareCampaignUse": "Unknown",
33
+ "notes": "https:\/\/support.citrix.com\/external\/article\/691941\/citrix-session-recording-security-bullet.html ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2024-8068",
34
+ "cwes": [
35
+ "CWE-269"
36
+ ]
37
+ },
38
+ {
39
+ "cveID": "CVE-2024-8069",
40
+ "vendorProject": "Citrix",
41
+ "product": "Session Recording",
42
+ "vulnerabilityName": "Citrix Session Recording Deserialization of Untrusted Data Vulnerability",
43
+ "dateAdded": "2025-08-25",
44
+ "shortDescription": "Citrix Session Recording contains a deserialization of untrusted data vulnerability that allows limited remote code execution with privilege of a NetworkService Account access. Attacker must be an authenticated user on the same intranet as the session recording server.",
45
+ "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
46
+ "dueDate": "2025-09-15",
47
+ "knownRansomwareCampaignUse": "Unknown",
48
+ "notes": "https:\/\/support.citrix.com\/external\/article\/691941\/citrix-session-recording-security-bullet.html ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2024-8069",
49
+ "cwes": [
50
+ "CWE-502"
51
+ ]
52
+ },
7
53
  {
8
54
  "cveID": "CVE-2025-43300",
9
55
  "vendorProject": "Apple",
@@ -3,7 +3,8 @@
3
3
  """Class for milestone model in RegScale platform"""
4
4
 
5
5
  from typing import Optional
6
- from pydantic import ConfigDict, Field
6
+
7
+ from pydantic import Field, field_validator
7
8
 
8
9
  from regscale.core.app.utils.app_utils import get_current_datetime
9
10
  from regscale.models.regscale_models.regscale_model import RegScaleModel
@@ -14,27 +15,27 @@ class Milestone(RegScaleModel):
14
15
 
15
16
  _module_slug = "milestones"
16
17
  _module_string = "milestones"
18
+ _unique_fields = ["title", "parentID", "parentModule"]
17
19
 
18
20
  title: str
19
21
  id: int = 0
20
22
  isPublic: Optional[bool] = True
21
23
  milestoneDate: Optional[str] = Field(default_factory=get_current_datetime)
22
24
  responsiblePersonId: Optional[str] = None
23
- predecessorStepId: Optional[int] = 0
25
+ predecessorStepId: Optional[int] = None
24
26
  completed: Optional[bool] = False
25
- dateCompleted: Optional[str] = Field(default_factory=get_current_datetime)
27
+ dateCompleted: Optional[str] = None
26
28
  notes: Optional[str] = ""
27
29
  parentID: Optional[int] = None
28
30
  parentModule: str = ""
29
31
 
30
- @staticmethod
31
- def _get_additional_endpoints() -> ConfigDict:
32
- """
33
- Get additional endpoints for the Milestone model
34
-
35
- :return: A dictionary of additional endpoints
36
- :rtype: ConfigDict
37
- """
38
- return ConfigDict(
39
- get_all_by_parent="/api/{model_slug}/getAllByParent/{intParentID}",
40
- )
32
+ @field_validator("dateCompleted")
33
+ @classmethod
34
+ def set_date_completed(cls, v: Optional[str], info) -> Optional[str]:
35
+ """Set dateCompleted based on completed field."""
36
+ completed = info.data.get("completed", False)
37
+ if completed and v is None:
38
+ return get_current_datetime()
39
+ if not completed:
40
+ return None
41
+ return v
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: regscale-cli
3
- Version: 6.21.2.0
3
+ Version: 6.21.2.1
4
4
  Summary: Command Line Interface (CLI) for bulk processing/loading data into RegScale
5
5
  Home-page: https://github.com/RegScale/regscale-cli
6
6
  Author: Travis Howerton
@@ -1,5 +1,5 @@
1
1
  regscale/__init__.py,sha256=ZygAIkX6Nbjag1czWdQa-yP-GM1mBE_9ss21Xh__JFc,34
2
- regscale/_version.py,sha256=RSHUslawemipH8yaOMvaSKPUKiKa27EuRFDlAk-t2k4,1198
2
+ regscale/_version.py,sha256=aKR5M6GR_hLtq7YS3vSDrCZwuydhT3NwAFRrOjyWNBY,1198
3
3
  regscale/regscale.py,sha256=xcxnTwEwWgfO3Fnp0LVo32SZCJzAswq3WDZgm21nHnI,30914
4
4
  regscale/airflow/__init__.py,sha256=yMwN0Bz4JbM0nl5qY_hPegxo_O2ilhTOL9PY5Njhn-s,270
5
5
  regscale/airflow/click_dags.py,sha256=H3SUR5jkvInNMv1gu-VG-Ja_H-kH145CpQYNalWNAbE,4520
@@ -36,7 +36,7 @@ regscale/core/lazy_group.py,sha256=S2-nA5tzm47A929NOTqGkzrzKuZQDlq2OAPbNnG1W1Q,2
36
36
  regscale/core/login.py,sha256=-8vy1HVAtv1iARnZh6uzYtwmx8VFYPwLYR0QAf1ttCk,2714
37
37
  regscale/core/app/__init__.py,sha256=nGcCN1vWBAnZzoccIlt0jwWQdegCOrBWOB7LPhQkQSs,96
38
38
  regscale/core/app/api.py,sha256=CSyUCV6haBAQ9IyE1FViJcAfTcoS5GJRaULwnRoAV9U,23499
39
- regscale/core/app/application.py,sha256=R86hqlOG5AI1kpLQr16cDsm9cZvBUz0FY9S9-gcU144,31867
39
+ regscale/core/app/application.py,sha256=O-Z0EVQuGIWAeUSjz2IK1LnUP5zF1YNOb4gxZ79B1LI,31903
40
40
  regscale/core/app/logz.py,sha256=8AdBKmquv45JGi5fCMc38JqQg6-FpUONGmqfd5EGwrI,2583
41
41
  regscale/core/app/internal/__init__.py,sha256=rod4nmE7rrYDdbuYF4mCWz49TK_2r-v4tWy1UHW63r0,4808
42
42
  regscale/core/app/internal/admin_actions.py,sha256=hOdma7QGwFblmxEj9UTjiWWmZGUS5_ZFtxL2qZdhf-Q,7443
@@ -111,7 +111,7 @@ regscale/exceptions/validation_exception.py,sha256=_DW_GARtPr_Dyy8tolnvC_AYsHRsU
111
111
  regscale/integrations/__init__.py,sha256=Sqthp3Jggo7co_go380cLn3OAb0cHwqL609_4QJSFBY,58
112
112
  regscale/integrations/api_paginator.py,sha256=73rjaNM9mGv8evHAeoObXEjZPg-bJuGPo60ewCLEil8,33192
113
113
  regscale/integrations/api_paginator_example.py,sha256=lEuYI-xEGcjnXuIzbCobCP0YRuukLF0s8S3d382SAH4,12119
114
- regscale/integrations/compliance_integration.py,sha256=8HPu9WRheJjoowP8fnTjJOGIFcB8qEwbP-qEGPDdAsY,68428
114
+ regscale/integrations/compliance_integration.py,sha256=aR13rcMh-BTpr_ibENPQZ1z5rlhtuqpPGRI9YfbqJjw,68394
115
115
  regscale/integrations/integration_override.py,sha256=HjYBCuvNpU3t3FptaqYAkdexLFOZBAFrFE9xU0RD1Nc,5867
116
116
  regscale/integrations/jsonl_scanner_integration.py,sha256=l8nq_T3rE1XX-6HxrNHm3xzxCNWbIjxQvGMdtZWs7KQ,57003
117
117
  regscale/integrations/scanner_integration.py,sha256=JpRr7LgzOdRdrncJ-wJrypgUTVWZ1j3Jbhl0zrNMvts,145540
@@ -240,7 +240,7 @@ regscale/integrations/commercial/wizv2/data_fetcher.py,sha256=tMdwWfaQCqLpZg9tGE
240
240
  regscale/integrations/commercial/wizv2/finding_processor.py,sha256=Rn2dYob4cz24_Ks-UTwNQe-KQ6hmFWS6GB8PQXV0ZPE,11361
241
241
  regscale/integrations/commercial/wizv2/issue.py,sha256=PA_7BNpCnHGoAc1caU6COGHaIIvCBvIt6DLn3L7Bilo,13451
242
242
  regscale/integrations/commercial/wizv2/parsers.py,sha256=dsSMiZaUrBXbuW7U-I5nLoF-TQlpvXys83sTSqT4Yks,11819
243
- regscale/integrations/commercial/wizv2/policy_compliance.py,sha256=GjqgQu8o3Hajd17nXcPyU9qsM-6ZDzNt2pQs6ZONzbg,129874
243
+ regscale/integrations/commercial/wizv2/policy_compliance.py,sha256=a2VzMxwGiGeIcU8FnyCH6I0z0wdyXeVQPFM7hV9Td-k,133181
244
244
  regscale/integrations/commercial/wizv2/policy_compliance_helpers.py,sha256=-KAlDxMiojyUX2DtUi3uVbpmFhDLtGs7wj11MPBCaRE,22174
245
245
  regscale/integrations/commercial/wizv2/sbom.py,sha256=QcGaYiBGtZ3mBcbo-KGl-I2u6QHKAIinTk26LPy0Kng,4466
246
246
  regscale/integrations/commercial/wizv2/scanner.py,sha256=weSjSBFvvgHmMJp4lpjgomgvBIJ7rjpg_7W7JW-ds-A,74566
@@ -320,7 +320,7 @@ regscale/models/integration_models/azure_alerts.py,sha256=2etrpvcxa7jVQrc98bJlVG
320
320
  regscale/models/integration_models/base64.py,sha256=sxV6O5qY1_TstJENX5jBPsSdQwmA83-NNhgJFunXiZE,570
321
321
  regscale/models/integration_models/burp.py,sha256=FBEBkH3U0Q8vq71FFoWnvgLRF5Hkr9GYmQFmNNHFrVk,16932
322
322
  regscale/models/integration_models/burp_models.py,sha256=UytDTAcCaxyu-knFkm_mEUH6UmWK3OTXKSC9Sc6OjVs,3669
323
- regscale/models/integration_models/cisa_kev_data.json,sha256=rS7XvjrNcm6PObHircKJqKgO01ZhN5XhqehYerDuphQ,1253951
323
+ regscale/models/integration_models/cisa_kev_data.json,sha256=kBsfInlBo4eaPjn8uFe7b3JSv51xtj7wjVCoNtWrG9I,1257565
324
324
  regscale/models/integration_models/defender_data.py,sha256=jsAcjKxiGmumGerj7xSWkFd6r__YpuKDnYX5o7xHDiE,2844
325
325
  regscale/models/integration_models/defenderimport.py,sha256=Ze4kgwns-IYPyO7sBjEzW8PXWlxwU-DAo2fIyRcTC3k,6242
326
326
  regscale/models/integration_models/drf.py,sha256=Aq7AdLa_CH97NrnR-CxaFI22JjVN9uCxVN7Z-BBUaNU,18896
@@ -415,7 +415,7 @@ regscale/models/regscale_models/line_of_inquiry.py,sha256=Uu0lQEhif0W6yTSkJo27Gy
415
415
  regscale/models/regscale_models/link.py,sha256=lAY4Ig3Menm1EqfcAbVJ7jsCsRO5tWtJIf-9-G9FXT8,6593
416
416
  regscale/models/regscale_models/master_assessment.py,sha256=t03-8vQJ7hnPNgEU0GSC1XGKOgAqHKuIupbt5IjJvtI,7322
417
417
  regscale/models/regscale_models/meta_data.py,sha256=Fg8rrWSTx3K00QkF4glH9UdY9OFWJ4_UqxleLSSbx8I,2482
418
- regscale/models/regscale_models/milestone.py,sha256=ooYVlao19fwwCXErhewwMvXqGjGLWW3EL_PQ_0lYQWY,1229
418
+ regscale/models/regscale_models/milestone.py,sha256=Mzf1BqG5S4a-EmgiXu8oqY8ssV7oSfgeknPI3_eINv0,1295
419
419
  regscale/models/regscale_models/module.py,sha256=a7lalHmVTQ04ZILnJxuFWdHYDtyusBMTtonD4ICL-Wc,7272
420
420
  regscale/models/regscale_models/modules.py,sha256=yeva_tct88o2NFt93_zmqUcXZ3LucVbAv5FvQPru3cQ,8551
421
421
  regscale/models/regscale_models/objective.py,sha256=aJIpmkZ-NscqV2y8SGB4HYzm615b6mklyHnW9bL2ljk,249
@@ -533,9 +533,9 @@ tests/regscale/models/test_regscale_model.py,sha256=ZsrEZkC4EtdIsoQuayn1xv2gEGcV
533
533
  tests/regscale/models/test_report.py,sha256=IqUq7C__a1_q_mLaz0PE9Lq6fHggBsB14-AzEYNBxLw,4666
534
534
  tests/regscale/models/test_tenable_integrations.py,sha256=PNJC2Zu6lv1xj7y6e1yOsz5FktSU3PRKb5x3n5YG3w0,4072
535
535
  tests/regscale/models/test_user_model.py,sha256=e9olv28qBApgnvK6hFHOgXjUC-pkaV8aGDirEIWASL4,4427
536
- regscale_cli-6.21.2.0.dist-info/LICENSE,sha256=ytNhYQ9Rmhj_m-EX2pPq9Ld6tH5wrqqDYg-fCf46WDU,1076
537
- regscale_cli-6.21.2.0.dist-info/METADATA,sha256=MqoS0sl4OZ364E34R6tvMovjED3MWz63xJwSoipU1EQ,34955
538
- regscale_cli-6.21.2.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
539
- regscale_cli-6.21.2.0.dist-info/entry_points.txt,sha256=cLOaIP1eRv1yZ2u7BvpE3aB4x3kDrDwkpeisKOu33z8,269
540
- regscale_cli-6.21.2.0.dist-info/top_level.txt,sha256=Uv8VUCAdxRm70bgrD4YNEJUmDhBThad_1aaEFGwRByc,15
541
- regscale_cli-6.21.2.0.dist-info/RECORD,,
536
+ regscale_cli-6.21.2.1.dist-info/LICENSE,sha256=ytNhYQ9Rmhj_m-EX2pPq9Ld6tH5wrqqDYg-fCf46WDU,1076
537
+ regscale_cli-6.21.2.1.dist-info/METADATA,sha256=u5fjU9V_avvh2jOU8-hXgxc-mHhkPM1gZvz65X3GI78,34955
538
+ regscale_cli-6.21.2.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
539
+ regscale_cli-6.21.2.1.dist-info/entry_points.txt,sha256=cLOaIP1eRv1yZ2u7BvpE3aB4x3kDrDwkpeisKOu33z8,269
540
+ regscale_cli-6.21.2.1.dist-info/top_level.txt,sha256=Uv8VUCAdxRm70bgrD4YNEJUmDhBThad_1aaEFGwRByc,15
541
+ regscale_cli-6.21.2.1.dist-info/RECORD,,