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

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

Potentially problematic release.


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

Files changed (95) 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/jira.py +367 -126
  6. regscale/integrations/commercial/qualys/__init__.py +7 -8
  7. regscale/integrations/commercial/qualys/scanner.py +8 -3
  8. regscale/integrations/commercial/synqly/assets.py +17 -0
  9. regscale/integrations/commercial/synqly/vulnerabilities.py +45 -28
  10. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  11. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  12. regscale/integrations/commercial/tenablev2/commands.py +142 -1
  13. regscale/integrations/commercial/tenablev2/scanner.py +0 -1
  14. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  15. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  16. regscale/integrations/commercial/wizv2/click.py +44 -59
  17. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  18. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  19. regscale/integrations/commercial/wizv2/compliance_report.py +10 -9
  20. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  21. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +3 -3
  22. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +1 -17
  23. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  24. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  25. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +5 -9
  26. regscale/integrations/commercial/wizv2/issue.py +1 -1
  27. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  28. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  29. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  30. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  31. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  32. regscale/integrations/commercial/wizv2/reports.py +1 -1
  33. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  34. regscale/integrations/commercial/wizv2/scanner.py +40 -100
  35. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  36. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  37. regscale/integrations/commercial/wizv2/variables.py +89 -3
  38. regscale/integrations/compliance_integration.py +0 -46
  39. regscale/integrations/control_matcher.py +22 -3
  40. regscale/integrations/due_date_handler.py +14 -8
  41. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  42. regscale/integrations/public/fedramp/fedramp_cis_crm.py +393 -340
  43. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  44. regscale/integrations/scanner_integration.py +127 -57
  45. regscale/models/integration_models/cisa_kev_data.json +132 -9
  46. regscale/models/integration_models/qualys.py +3 -4
  47. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  48. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +24 -7
  49. regscale/models/integration_models/synqly_models/synqly_model.py +8 -1
  50. regscale/models/regscale_models/control_implementation.py +1 -1
  51. regscale/models/regscale_models/issue.py +0 -1
  52. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/METADATA +1 -17
  53. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/RECORD +93 -60
  54. tests/regscale/integrations/commercial/test_jira.py +481 -91
  55. tests/regscale/integrations/commercial/test_wiz.py +96 -200
  56. tests/regscale/integrations/commercial/wizv2/__init__.py +1 -1
  57. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  58. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  59. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  60. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  61. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  62. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  63. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  64. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  65. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  66. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  67. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  68. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  69. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  70. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  71. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  72. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  73. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  74. tests/regscale/integrations/commercial/wizv2/test_issue.py +1 -1
  75. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  76. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  77. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  78. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  79. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +1 -1
  80. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +72 -29
  81. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  82. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  83. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +946 -78
  84. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +97 -202
  85. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  86. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  87. tests/regscale/integrations/public/test_fedramp.py +301 -0
  88. tests/regscale/integrations/test_control_matcher.py +83 -0
  89. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3543
  90. tests/regscale/integrations/commercial/wizv2/test_wiz_policy_compliance.py +0 -750
  91. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  92. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/LICENSE +0 -0
  93. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/WHEEL +0 -0
  94. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/entry_points.txt +0 -0
  95. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/top_level.txt +0 -0
@@ -93,7 +93,7 @@ class TestWizClientIdHandling:
93
93
  @pytest.fixture
94
94
  def mock_wiz_auth(self):
95
95
  """Mock wiz_authenticate."""
96
- with patch("regscale.integrations.commercial.wizv2.wiz_auth.wiz_authenticate") as mock:
96
+ with patch("regscale.integrations.commercial.wizv2.core.auth.wiz_authenticate") as mock:
97
97
  yield mock
98
98
 
99
99
  @pytest.fixture
@@ -481,6 +481,7 @@ class TestWizComplianceReportItem(unittest.TestCase):
481
481
 
482
482
 
483
483
  @pytest.mark.no_parallel
484
+ @patch("regscale.integrations.compliance_integration.ComplianceIntegration.__init__", return_value=None)
484
485
  class TestWizComplianceReportProcessor(unittest.TestCase):
485
486
  """Test suite for WizComplianceReportProcessor class."""
486
487
 
@@ -527,9 +528,21 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
527
528
  },
528
529
  ]
529
530
 
531
+ def _initialize_processor_attributes(self, processor):
532
+ """Initialize parent class attributes that would normally be set by __init__."""
533
+ from collections import defaultdict
534
+
535
+ processor.all_compliance_items = []
536
+ processor.failed_compliance_items = []
537
+ processor.passing_controls = {}
538
+ processor.failing_controls = {}
539
+ processor.asset_compliance_map = defaultdict(list)
540
+ processor.plan_id = self.plan_id
541
+ processor.title = "Wiz Compliance"
542
+
530
543
  @patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager")
531
544
  @patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
532
- def test_init_successful_authentication(self, mock_auth, mock_report_manager):
545
+ def test_init_successful_authentication(self, mock_auth, mock_report_manager, mock_parent_init):
533
546
  """Test successful initialization with authentication."""
534
547
  mock_auth.return_value = "test-token"
535
548
  mock_report_manager_instance = MagicMock()
@@ -541,6 +554,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
541
554
  client_id=self.client_id,
542
555
  client_secret=self.client_secret,
543
556
  )
557
+ self._initialize_processor_attributes(processor)
544
558
 
545
559
  mock_auth.assert_called_once_with(self.client_id, self.client_secret)
546
560
  mock_report_manager.assert_called_once()
@@ -551,7 +565,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
551
565
  @patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager")
552
566
  @patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
553
567
  @patch("regscale.integrations.commercial.wizv2.compliance_report.error_and_exit")
554
- def test_init_failed_authentication(self, mock_error_exit, mock_auth, mock_report_manager):
568
+ def test_init_failed_authentication(self, mock_error_exit, mock_auth, mock_report_manager, mock_parent_init):
555
569
  """Test initialization with failed authentication."""
556
570
  mock_auth.return_value = None
557
571
  mock_error_exit.side_effect = SystemExit(1)
@@ -566,7 +580,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
566
580
 
567
581
  mock_error_exit.assert_called_once_with("Failed to authenticate with Wiz")
568
582
 
569
- def test_parse_csv_report_regular_file(self):
583
+ def test_parse_csv_report_regular_file(self, mock_parent_init):
570
584
  """Test parse_csv_report with regular CSV file."""
571
585
  # Create temporary CSV file
572
586
  with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False) as temp_file:
@@ -599,7 +613,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
599
613
  finally:
600
614
  os.unlink(temp_file_path)
601
615
 
602
- def test_parse_csv_report_gzipped_file(self):
616
+ def test_parse_csv_report_gzipped_file(self, mock_parent_init):
603
617
  """Test parse_csv_report with gzipped CSV file."""
604
618
  # Create temporary gzipped CSV file
605
619
  with tempfile.NamedTemporaryFile(suffix=".csv.gz", delete=False) as temp_file:
@@ -631,7 +645,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
631
645
  finally:
632
646
  os.unlink(temp_file_path)
633
647
 
634
- def test_parse_csv_report_file_not_found(self):
648
+ def test_parse_csv_report_file_not_found(self, mock_parent_init):
635
649
  """Test parse_csv_report with non-existent file."""
636
650
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
637
651
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -647,7 +661,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
647
661
 
648
662
  @patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager")
649
663
  @patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
650
- def test_fetch_compliance_data_with_existing_report(self, mock_auth, mock_report_manager):
664
+ def test_fetch_compliance_data_with_existing_report(self, mock_auth, mock_report_manager, mock_parent_init):
651
665
  """Test fetch_compliance_data with existing report file."""
652
666
  mock_auth.return_value = "test-token"
653
667
 
@@ -678,7 +692,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
678
692
 
679
693
  @patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager")
680
694
  @patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
681
- def test_fetch_compliance_data_report_creation(self, mock_auth, mock_report_manager):
695
+ def test_fetch_compliance_data_report_creation(self, mock_auth, mock_report_manager, mock_parent_init):
682
696
  """Test fetch_compliance_data creates new report when none exists."""
683
697
  mock_auth.return_value = "test-token"
684
698
 
@@ -706,7 +720,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
706
720
  finally:
707
721
  os.unlink(temp_file_path)
708
722
 
709
- def test_fetch_compliance_data_file_read_error(self):
723
+ def test_fetch_compliance_data_file_read_error(self, mock_parent_init):
710
724
  """Test fetch_compliance_data handles file read errors."""
711
725
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
712
726
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -723,7 +737,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
723
737
  raw_data = processor.fetch_compliance_data()
724
738
  self.assertEqual(len(raw_data), 0)
725
739
 
726
- def test_create_compliance_item(self):
740
+ def test_create_compliance_item(self, mock_parent_init):
727
741
  """Test create_compliance_item creates WizComplianceReportItem."""
728
742
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
729
743
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -740,7 +754,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
740
754
 
741
755
  @patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager")
742
756
  @patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
743
- def test_process_compliance_data_bypass_filtering(self, mock_auth, mock_report_manager):
757
+ def test_process_compliance_data_bypass_filtering(self, mock_auth, mock_report_manager, mock_parent_init):
744
758
  """Test process_compliance_data with bypass_control_filtering=True."""
745
759
  mock_auth.return_value = "test-token"
746
760
 
@@ -758,7 +772,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
758
772
 
759
773
  @patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager")
760
774
  @patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
761
- def test_process_compliance_data_normal_filtering(self, mock_auth, mock_report_manager):
775
+ def test_process_compliance_data_normal_filtering(self, mock_auth, mock_report_manager, mock_parent_init):
762
776
  """Test process_compliance_data with normal filtering."""
763
777
  mock_auth.return_value = "test-token"
764
778
 
@@ -775,7 +789,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
775
789
  processor.process_compliance_data()
776
790
  mock_parent.assert_called_once()
777
791
 
778
- def test_process_compliance_data_without_filtering_categorization(self):
792
+ def test_process_compliance_data_without_filtering_categorization(self, mock_parent_init):
779
793
  """Test _process_compliance_data_without_filtering categorizes controls correctly."""
780
794
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
781
795
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -785,6 +799,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
785
799
  client_id=self.client_id,
786
800
  client_secret=self.client_secret,
787
801
  )
802
+ self._initialize_processor_attributes(processor)
788
803
 
789
804
  # Mock fetch_compliance_data to return our test data
790
805
  with patch.object(processor, "fetch_compliance_data", return_value=self.sample_csv_data):
@@ -798,7 +813,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
798
813
  # Should call fail-first categorization
799
814
  mock_categorize.assert_called_once()
800
815
 
801
- def test_process_compliance_sync(self):
816
+ def test_process_compliance_sync(self, mock_parent_init):
802
817
  """Test process_compliance_sync calls sync_compliance."""
803
818
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
804
819
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -813,7 +828,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
813
828
  processor.process_compliance_sync()
814
829
  mock_sync.assert_called_once()
815
830
 
816
- def test_get_or_create_report_existing_recent_report(self):
831
+ def test_get_or_create_report_existing_recent_report(self, mock_parent_init):
817
832
  """Test _get_or_create_report uses existing recent report."""
818
833
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
819
834
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -828,7 +843,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
828
843
  result = processor._get_or_create_report()
829
844
  self.assertEqual(result, "existing_report.csv")
830
845
 
831
- def test_get_or_create_report_creates_new_report(self):
846
+ def test_get_or_create_report_creates_new_report(self, mock_parent_init):
832
847
  """Test _get_or_create_report creates new report when none exists."""
833
848
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
834
849
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -844,7 +859,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
844
859
  result = processor._get_or_create_report()
845
860
  self.assertEqual(result, "new_report.csv")
846
861
 
847
- def test_find_recent_report_no_artifacts_dir(self):
862
+ def test_find_recent_report_no_artifacts_dir(self, mock_parent_init):
848
863
  """Test _find_recent_report returns None when artifacts directory doesn't exist."""
849
864
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
850
865
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -859,7 +874,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
859
874
  result = processor._find_recent_report()
860
875
  self.assertIsNone(result)
861
876
 
862
- def test_find_recent_report_finds_recent_file(self):
877
+ def test_find_recent_report_finds_recent_file(self, mock_parent_init):
863
878
  """Test _find_recent_report finds and returns recent file."""
864
879
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
865
880
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -880,7 +895,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
880
895
  expected_path = f"artifacts/wiz/{expected_filename}"
881
896
  self.assertEqual(result, expected_path)
882
897
 
883
- def test_find_recent_report_ignores_old_files(self):
898
+ def test_find_recent_report_ignores_old_files(self, mock_parent_init):
884
899
  """Test _find_recent_report ignores files older than max_age_hours."""
885
900
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
886
901
  with patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager"):
@@ -903,7 +918,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
903
918
 
904
919
  @patch("regscale.integrations.commercial.wizv2.compliance_report.ReportFileCleanup")
905
920
  @patch("os.makedirs")
906
- def test_create_and_download_report_success(self, mock_makedirs, mock_cleanup):
921
+ def test_create_and_download_report_success(self, mock_makedirs, mock_cleanup, mock_parent_init):
907
922
  """Test _create_and_download_report successful report creation."""
908
923
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
909
924
  mock_report_manager = MagicMock()
@@ -931,7 +946,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
931
946
  mock_makedirs.assert_called_once()
932
947
  mock_cleanup.cleanup_old_files.assert_called_once()
933
948
 
934
- def test_create_and_download_report_creation_failure(self):
949
+ def test_create_and_download_report_creation_failure(self, mock_parent_init):
935
950
  """Test _create_and_download_report handles report creation failure."""
936
951
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
937
952
  mock_report_manager = MagicMock()
@@ -952,7 +967,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
952
967
  result = processor._create_and_download_report()
953
968
  self.assertIsNone(result)
954
969
 
955
- def test_create_and_download_report_download_failure(self):
970
+ def test_create_and_download_report_download_failure(self, mock_parent_init):
956
971
  """Test _create_and_download_report handles download failure."""
957
972
  with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate", return_value="token"):
958
973
  mock_report_manager = MagicMock()
@@ -978,6 +993,7 @@ class TestWizComplianceReportProcessor(unittest.TestCase):
978
993
 
979
994
 
980
995
  @pytest.mark.no_parallel
996
+ @patch("regscale.integrations.compliance_integration.ComplianceIntegration.__init__", return_value=None)
981
997
  class TestWizComplianceStatusMatching(unittest.TestCase):
982
998
  """Test suite for status matching and case-insensitive logic."""
983
999
 
@@ -1000,7 +1016,17 @@ class TestWizComplianceStatusMatching(unittest.TestCase):
1000
1016
  "Remediation Steps": "Fix this",
1001
1017
  }
1002
1018
 
1003
- def test_pass_status_matching_case_insensitive(self):
1019
+ def _initialize_processor_attributes(self, processor):
1020
+ """Initialize parent class attributes that would normally be set by __init__."""
1021
+ from collections import defaultdict
1022
+
1023
+ processor.all_compliance_items = []
1024
+ processor.failed_compliance_items = []
1025
+ processor.passing_controls = {}
1026
+ processor.failing_controls = {}
1027
+ processor.asset_compliance_map = defaultdict(list)
1028
+
1029
+ def test_pass_status_matching_case_insensitive(self, mock_parent_init):
1004
1030
  """Test that pass statuses are matched case-insensitively."""
1005
1031
  # Current implementation supports: Pass, PASS, pass but not Passed, PASSED, passed
1006
1032
  pass_values = ["Pass", "PASS", "pass"]
@@ -1025,7 +1051,7 @@ class TestWizComplianceStatusMatching(unittest.TestCase):
1025
1051
  self.assertEqual(item.get_status(), "Other Than Satisfied")
1026
1052
  self.assertEqual(item.get_implementation_status(), "In Remediation")
1027
1053
 
1028
- def test_fail_status_matching_case_insensitive(self):
1054
+ def test_fail_status_matching_case_insensitive(self, mock_parent_init):
1029
1055
  """Test that fail statuses are matched case-insensitively."""
1030
1056
  fail_values = ["Fail", "FAIL", "fail", "Failed", "FAILED", "failed"]
1031
1057
 
@@ -1037,7 +1063,7 @@ class TestWizComplianceStatusMatching(unittest.TestCase):
1037
1063
  self.assertEqual(item.get_status(), "Other Than Satisfied")
1038
1064
  self.assertEqual(item.get_implementation_status(), "In Remediation")
1039
1065
 
1040
- def test_status_categorization_in_processor(self):
1066
+ def test_status_categorization_in_processor(self, mock_parent_init):
1041
1067
  """Test that WizComplianceReportProcessor correctly categorizes statuses."""
1042
1068
  # Test data with mixed case statuses
1043
1069
  test_data = [
@@ -1058,6 +1084,7 @@ class TestWizComplianceStatusMatching(unittest.TestCase):
1058
1084
  client_secret="secret",
1059
1085
  bypass_control_filtering=False, # Use threshold-based logic
1060
1086
  )
1087
+ self._initialize_processor_attributes(processor)
1061
1088
 
1062
1089
  # Mock fetch_compliance_data to return test data
1063
1090
  with patch.object(processor, "fetch_compliance_data", return_value=test_data):
@@ -1074,10 +1101,21 @@ class TestWizComplianceStatusMatching(unittest.TestCase):
1074
1101
 
1075
1102
 
1076
1103
  @pytest.mark.no_parallel
1104
+ @patch("regscale.integrations.compliance_integration.ComplianceIntegration.__init__", return_value=None)
1077
1105
  class TestWizComplianceControlCategorization(unittest.TestCase):
1078
1106
  """Test suite for control categorization and aggregation logic."""
1079
1107
 
1080
- def test_control_categorization_all_pass(self):
1108
+ def _initialize_processor_attributes(self, processor):
1109
+ """Initialize parent class attributes that would normally be set by __init__."""
1110
+ from collections import defaultdict
1111
+
1112
+ processor.all_compliance_items = []
1113
+ processor.failed_compliance_items = []
1114
+ processor.passing_controls = {}
1115
+ processor.failing_controls = {}
1116
+ processor.asset_compliance_map = defaultdict(list)
1117
+
1118
+ def test_control_categorization_all_pass(self, mock_parent_init):
1081
1119
  """Test control categorization when all items pass."""
1082
1120
  test_data = [
1083
1121
  {
@@ -1109,6 +1147,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1109
1147
  client_secret="secret",
1110
1148
  bypass_control_filtering=False, # Use threshold-based logic
1111
1149
  )
1150
+ self._initialize_processor_attributes(processor)
1112
1151
 
1113
1152
  # Fill in required fields for test data
1114
1153
  full_test_data = []
@@ -1139,7 +1178,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1139
1178
  self.assertEqual(len(processor.failing_controls), 0)
1140
1179
  self.assertIn("ac-2(1)", processor.passing_controls)
1141
1180
 
1142
- def test_control_categorization_all_fail(self):
1181
+ def test_control_categorization_all_fail(self, mock_parent_init):
1143
1182
  """Test control categorization when all items fail."""
1144
1183
  test_data = [
1145
1184
  {
@@ -1171,6 +1210,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1171
1210
  client_secret="secret",
1172
1211
  bypass_control_filtering=False, # Use threshold-based logic
1173
1212
  )
1213
+ self._initialize_processor_attributes(processor)
1174
1214
 
1175
1215
  # Fill in required fields for test data
1176
1216
  full_test_data = []
@@ -1201,7 +1241,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1201
1241
  self.assertEqual(len(processor.failing_controls), 1)
1202
1242
  self.assertIn("ac-3", processor.failing_controls)
1203
1243
 
1204
- def test_control_categorization_mixed_results_high_failure_rate(self):
1244
+ def test_control_categorization_mixed_results_high_failure_rate(self, mock_parent_init):
1205
1245
  """Test control categorization with mixed results above failure threshold."""
1206
1246
  # 5 items: 2 pass, 3 fail = 60% failure rate (above default 20% threshold)
1207
1247
  test_data = [
@@ -1221,6 +1261,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1221
1261
  client_secret="secret",
1222
1262
  bypass_control_filtering=False, # Use threshold-based logic
1223
1263
  )
1264
+ self._initialize_processor_attributes(processor)
1224
1265
 
1225
1266
  # Fill in required fields - all same control
1226
1267
  full_test_data = []
@@ -1251,7 +1292,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1251
1292
  self.assertEqual(len(processor.failing_controls), 1)
1252
1293
  self.assertIn("ac-4", processor.failing_controls)
1253
1294
 
1254
- def test_control_categorization_mixed_results_fail_first(self):
1295
+ def test_control_categorization_mixed_results_fail_first(self, mock_parent_init):
1255
1296
  """Test control categorization with mixed results using fail-first logic."""
1256
1297
  # 10 items: 9 pass, 1 fail - with fail-first logic, any failure makes the control fail
1257
1298
  test_data = []
@@ -1268,6 +1309,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1268
1309
  client_secret="secret",
1269
1310
  bypass_control_filtering=False,
1270
1311
  )
1312
+ self._initialize_processor_attributes(processor)
1271
1313
 
1272
1314
  # Fill in required fields - all same control
1273
1315
  full_test_data = []
@@ -1298,7 +1340,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1298
1340
  self.assertEqual(len(processor.failing_controls), 1)
1299
1341
  self.assertIn("si-2", processor.failing_controls)
1300
1342
 
1301
- def test_control_categorization_fail_first_logic(self):
1343
+ def test_control_categorization_fail_first_logic(self, mock_parent_init):
1302
1344
  """Test that Wiz compliance uses fail-first logic regardless of bypass_control_filtering setting."""
1303
1345
  # 10 items: 7 pass, 3 fail - with fail-first logic, this should be a failing control
1304
1346
  test_data = []
@@ -1316,6 +1358,7 @@ class TestWizComplianceControlCategorization(unittest.TestCase):
1316
1358
  client_secret="secret",
1317
1359
  bypass_control_filtering=False,
1318
1360
  )
1361
+ self._initialize_processor_attributes(processor)
1319
1362
 
1320
1363
  # Fill in required fields - all same control
1321
1364
  full_test_data = []