regscale-cli 6.21.2.0__py3-none-any.whl → 6.28.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.
- regscale/_version.py +1 -1
- regscale/airflow/hierarchy.py +2 -2
- regscale/core/app/api.py +5 -2
- regscale/core/app/application.py +36 -6
- regscale/core/app/internal/control_editor.py +73 -21
- regscale/core/app/internal/evidence.py +727 -204
- regscale/core/app/internal/login.py +4 -2
- regscale/core/app/internal/model_editor.py +219 -64
- regscale/core/app/utils/app_utils.py +86 -12
- regscale/core/app/utils/catalog_utils/common.py +1 -1
- regscale/core/login.py +21 -4
- regscale/core/utils/async_graphql_client.py +363 -0
- regscale/core/utils/date.py +77 -1
- regscale/dev/cli.py +26 -0
- regscale/dev/code_gen.py +109 -24
- regscale/dev/version.py +72 -0
- regscale/integrations/commercial/__init__.py +30 -2
- regscale/integrations/commercial/aws/audit_manager_compliance.py +3908 -0
- regscale/integrations/commercial/aws/cli.py +3107 -54
- regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
- regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
- regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
- regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
- regscale/integrations/commercial/{amazon → aws}/common.py +71 -19
- regscale/integrations/commercial/aws/config_compliance.py +914 -0
- regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
- regscale/integrations/commercial/aws/control_compliance_analyzer.py +439 -0
- regscale/integrations/commercial/aws/evidence_generator.py +283 -0
- regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
- regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
- regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
- regscale/integrations/commercial/aws/iam_evidence.py +574 -0
- regscale/integrations/commercial/aws/inventory/__init__.py +338 -22
- regscale/integrations/commercial/aws/inventory/base.py +107 -5
- regscale/integrations/commercial/aws/inventory/resources/analytics.py +390 -0
- regscale/integrations/commercial/aws/inventory/resources/applications.py +234 -0
- regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
- regscale/integrations/commercial/aws/inventory/resources/compute.py +328 -9
- regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
- regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
- regscale/integrations/commercial/aws/inventory/resources/database.py +481 -31
- regscale/integrations/commercial/aws/inventory/resources/developer_tools.py +253 -0
- regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
- regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
- regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
- regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
- regscale/integrations/commercial/aws/inventory/resources/machine_learning.py +358 -0
- regscale/integrations/commercial/aws/inventory/resources/networking.py +390 -67
- regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
- regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
- regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
- regscale/integrations/commercial/aws/inventory/resources/storage.py +288 -29
- regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
- regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
- regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
- regscale/integrations/commercial/aws/kms_evidence.py +879 -0
- regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
- regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
- regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
- regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
- regscale/integrations/commercial/aws/org_evidence.py +666 -0
- regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
- regscale/integrations/commercial/aws/s3_evidence.py +632 -0
- regscale/integrations/commercial/aws/scanner.py +1072 -205
- regscale/integrations/commercial/aws/security_hub.py +319 -0
- regscale/integrations/commercial/aws/session_manager.py +282 -0
- regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
- regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
- regscale/integrations/commercial/jira.py +489 -153
- regscale/integrations/commercial/microsoft_defender/defender.py +326 -5
- regscale/integrations/commercial/microsoft_defender/defender_api.py +348 -14
- regscale/integrations/commercial/microsoft_defender/defender_constants.py +157 -0
- regscale/integrations/commercial/qualys/__init__.py +167 -68
- regscale/integrations/commercial/qualys/scanner.py +305 -39
- regscale/integrations/commercial/sarif/sairf_importer.py +432 -0
- regscale/integrations/commercial/sarif/sarif_converter.py +67 -0
- regscale/integrations/commercial/sicura/api.py +79 -42
- regscale/integrations/commercial/sicura/commands.py +8 -2
- regscale/integrations/commercial/sicura/scanner.py +83 -44
- regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
- regscale/integrations/commercial/synqly/assets.py +133 -16
- regscale/integrations/commercial/synqly/edr.py +2 -8
- regscale/integrations/commercial/synqly/query_builder.py +536 -0
- regscale/integrations/commercial/synqly/ticketing.py +27 -0
- regscale/integrations/commercial/synqly/vulnerabilities.py +165 -28
- regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
- regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
- regscale/integrations/commercial/tenablev2/commands.py +146 -5
- regscale/integrations/commercial/tenablev2/scanner.py +1 -3
- regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
- regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
- regscale/integrations/commercial/wizv2/click.py +191 -76
- regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
- regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
- regscale/integrations/commercial/wizv2/compliance_report.py +1592 -0
- regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
- regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +7 -3
- regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +92 -89
- regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
- regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +66 -9
- regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
- regscale/integrations/commercial/wizv2/issue.py +776 -28
- regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
- regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
- regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
- regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
- regscale/integrations/commercial/wizv2/reports.py +243 -0
- regscale/integrations/commercial/wizv2/sbom.py +1 -1
- regscale/integrations/commercial/wizv2/scanner.py +1031 -441
- regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
- regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
- regscale/integrations/commercial/wizv2/variables.py +89 -3
- regscale/integrations/compliance_integration.py +1036 -151
- regscale/integrations/control_matcher.py +432 -0
- regscale/integrations/due_date_handler.py +333 -0
- regscale/integrations/milestone_manager.py +291 -0
- regscale/integrations/public/__init__.py +14 -0
- regscale/integrations/public/cci_importer.py +834 -0
- regscale/integrations/public/csam/__init__.py +0 -0
- regscale/integrations/public/csam/csam.py +938 -0
- regscale/integrations/public/csam/csam_agency_defined.py +179 -0
- regscale/integrations/public/csam/csam_common.py +154 -0
- regscale/integrations/public/csam/csam_controls.py +432 -0
- regscale/integrations/public/csam/csam_poam.py +124 -0
- regscale/integrations/public/fedramp/click.py +77 -6
- regscale/integrations/public/fedramp/docx_parser.py +10 -1
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +675 -289
- regscale/integrations/public/fedramp/fedramp_five.py +1 -1
- regscale/integrations/public/fedramp/poam/scanner.py +75 -7
- regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
- regscale/integrations/scanner_integration.py +1961 -430
- regscale/models/integration_models/CCI_List.xml +1 -0
- regscale/models/integration_models/aqua.py +2 -2
- regscale/models/integration_models/cisa_kev_data.json +805 -11
- regscale/models/integration_models/flat_file_importer/__init__.py +5 -8
- regscale/models/integration_models/nexpose.py +36 -10
- regscale/models/integration_models/qualys.py +3 -4
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +87 -18
- regscale/models/integration_models/synqly_models/filter_parser.py +332 -0
- regscale/models/integration_models/synqly_models/ocsf_mapper.py +124 -25
- regscale/models/integration_models/synqly_models/synqly_model.py +89 -16
- regscale/models/locking.py +12 -8
- regscale/models/platform.py +4 -2
- regscale/models/regscale_models/__init__.py +7 -0
- regscale/models/regscale_models/assessment.py +2 -1
- regscale/models/regscale_models/catalog.py +1 -1
- regscale/models/regscale_models/compliance_settings.py +251 -1
- regscale/models/regscale_models/component.py +1 -0
- regscale/models/regscale_models/control_implementation.py +236 -41
- regscale/models/regscale_models/control_objective.py +74 -5
- regscale/models/regscale_models/file.py +2 -0
- regscale/models/regscale_models/form_field_value.py +5 -3
- regscale/models/regscale_models/inheritance.py +44 -0
- regscale/models/regscale_models/issue.py +301 -102
- regscale/models/regscale_models/milestone.py +33 -14
- regscale/models/regscale_models/organization.py +3 -0
- regscale/models/regscale_models/regscale_model.py +310 -73
- regscale/models/regscale_models/security_plan.py +4 -2
- regscale/models/regscale_models/vulnerability.py +3 -3
- regscale/regscale.py +25 -4
- regscale/templates/__init__.py +0 -0
- regscale/utils/threading/threadhandler.py +20 -15
- regscale/validation/record.py +23 -1
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/METADATA +17 -33
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/RECORD +310 -111
- tests/core/__init__.py +0 -0
- tests/core/utils/__init__.py +0 -0
- tests/core/utils/test_async_graphql_client.py +472 -0
- tests/fixtures/test_fixture.py +13 -8
- tests/regscale/core/test_login.py +171 -4
- tests/regscale/integrations/commercial/__init__.py +0 -0
- tests/regscale/integrations/commercial/aws/__init__.py +0 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
- tests/regscale/integrations/commercial/aws/test_aws_analytics_collector.py +260 -0
- tests/regscale/integrations/commercial/aws/test_aws_applications_collector.py +242 -0
- tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
- tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
- tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
- tests/regscale/integrations/commercial/aws/test_aws_developer_tools_collector.py +203 -0
- tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
- tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
- tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
- tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
- tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
- tests/regscale/integrations/commercial/aws/test_aws_machine_learning_collector.py +237 -0
- tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
- tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
- tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
- tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
- tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
- tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
- tests/regscale/integrations/commercial/aws/test_control_compliance_analyzer.py +375 -0
- tests/regscale/integrations/commercial/aws/test_datetime_parsing.py +223 -0
- tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
- tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
- tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
- tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
- tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
- tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
- tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
- tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
- tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
- tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
- tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
- tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
- tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
- tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
- tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
- tests/regscale/integrations/commercial/conftest.py +28 -0
- tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
- tests/regscale/integrations/commercial/test_aws.py +3742 -0
- tests/regscale/integrations/commercial/test_burp.py +48 -0
- tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
- tests/regscale/integrations/commercial/test_dependabot.py +341 -0
- tests/regscale/integrations/commercial/test_gcp.py +1543 -0
- tests/regscale/integrations/commercial/test_gitlab.py +549 -0
- tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
- tests/regscale/integrations/commercial/test_jira.py +2204 -0
- tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
- tests/regscale/integrations/commercial/test_okta.py +1228 -0
- tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
- tests/regscale/integrations/commercial/test_sicura.py +349 -0
- tests/regscale/integrations/commercial/test_snow.py +423 -0
- tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
- tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
- tests/regscale/integrations/commercial/test_stig.py +33 -0
- tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
- tests/regscale/integrations/commercial/test_stigv2.py +406 -0
- tests/regscale/integrations/commercial/test_wiz.py +1365 -0
- tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
- tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
- tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
- tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
- tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
- tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
- tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
- tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
- tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
- tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
- tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
- tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
- tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
- tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
- tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
- tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
- tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
- tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
- tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1218 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
- tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
- tests/regscale/integrations/public/__init__.py +0 -0
- tests/regscale/integrations/public/fedramp/__init__.py +1 -0
- tests/regscale/integrations/public/fedramp/test_gen_asset_list.py +150 -0
- tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
- tests/regscale/integrations/public/test_alienvault.py +220 -0
- tests/regscale/integrations/public/test_cci.py +1053 -0
- tests/regscale/integrations/public/test_cisa.py +1021 -0
- tests/regscale/integrations/public/test_emass.py +518 -0
- tests/regscale/integrations/public/test_fedramp.py +1152 -0
- tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
- tests/regscale/integrations/public/test_file_uploads.py +506 -0
- tests/regscale/integrations/public/test_oscal.py +453 -0
- tests/regscale/integrations/test_compliance_status_mapping.py +406 -0
- tests/regscale/integrations/test_control_matcher.py +1421 -0
- tests/regscale/integrations/test_control_matching.py +155 -0
- tests/regscale/integrations/test_milestone_manager.py +408 -0
- tests/regscale/models/test_control_implementation.py +118 -3
- tests/regscale/models/test_form_field_value_integration.py +304 -0
- tests/regscale/models/test_issue.py +378 -1
- tests/regscale/models/test_module_integration.py +582 -0
- tests/regscale/models/test_tenable_integrations.py +811 -105
- regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3057
- regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +0 -7388
- regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +0 -9605
- regscale/integrations/public/fedramp/parts_mapper.py +0 -107
- /regscale/integrations/commercial/{amazon → sarif}/__init__.py +0 -0
- /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/LICENSE +0 -0
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/WHEEL +0 -0
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Comprehensive unit tests for Wiz Compliance Report Integration.
|
|
5
|
+
|
|
6
|
+
This test suite provides extensive coverage of WizComplianceReportItem and
|
|
7
|
+
WizComplianceReportProcessor classes, focusing on:
|
|
8
|
+
- CSV parsing and data transformation
|
|
9
|
+
- Control mapping and categorization
|
|
10
|
+
- Issue creation from compliance items
|
|
11
|
+
- Report file management
|
|
12
|
+
- Control implementation status updates
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import csv
|
|
16
|
+
import gzip
|
|
17
|
+
import os
|
|
18
|
+
import tempfile
|
|
19
|
+
import unittest
|
|
20
|
+
from collections import defaultdict
|
|
21
|
+
from datetime import datetime, timedelta
|
|
22
|
+
from unittest.mock import MagicMock, Mock, PropertyMock, call, mock_open, patch
|
|
23
|
+
|
|
24
|
+
import pytest
|
|
25
|
+
|
|
26
|
+
from regscale.integrations.commercial.wizv2.compliance_report import (
|
|
27
|
+
WizComplianceReportItem,
|
|
28
|
+
WizComplianceReportProcessor,
|
|
29
|
+
)
|
|
30
|
+
from regscale.models import regscale_models
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TestWizComplianceReportItemComprehensive(unittest.TestCase):
|
|
34
|
+
"""Comprehensive test suite for WizComplianceReportItem class."""
|
|
35
|
+
|
|
36
|
+
def setUp(self):
|
|
37
|
+
"""Set up test fixtures."""
|
|
38
|
+
self.sample_csv_row = {
|
|
39
|
+
"Resource Name": "test-vm-001",
|
|
40
|
+
"Cloud Provider": "Azure",
|
|
41
|
+
"Cloud Provider ID": "subscriptions/12345/resourceGroups/rg-test/providers/Microsoft.Compute/virtualMachines/test-vm-001",
|
|
42
|
+
"Resource ID": "/subscriptions/12345/resourceGroups/rg-test/providers/Microsoft.Compute/virtualMachines/test-vm-001",
|
|
43
|
+
"Resource Region": "East US",
|
|
44
|
+
"Subscription": "subscription-123",
|
|
45
|
+
"Subscription Name": "Dev Subscription",
|
|
46
|
+
"Policy Name": "Ensure VM disk encryption is enabled",
|
|
47
|
+
"Policy ID": "policy-disk-encryption-001",
|
|
48
|
+
"Result": "Pass",
|
|
49
|
+
"Severity": "HIGH",
|
|
50
|
+
"Compliance Check Name (Wiz Subcategory)": "AC-2(1) Account Management | Automated System Account Management",
|
|
51
|
+
"Framework": "NIST SP 800-53 Revision 5",
|
|
52
|
+
"Remediation Steps": "Enable disk encryption on the VM through Azure portal",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def test_get_status_pass(self):
|
|
56
|
+
"""Test get_status returns 'Satisfied' for Pass result."""
|
|
57
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
58
|
+
self.assertEqual(item.get_status(), "Satisfied")
|
|
59
|
+
|
|
60
|
+
def test_get_status_fail(self):
|
|
61
|
+
"""Test get_status returns 'Other Than Satisfied' for Fail result."""
|
|
62
|
+
csv_row = {**self.sample_csv_row, "Result": "Fail"}
|
|
63
|
+
item = WizComplianceReportItem(csv_row)
|
|
64
|
+
self.assertEqual(item.get_status(), "Other Than Satisfied")
|
|
65
|
+
|
|
66
|
+
def test_get_implementation_status_pass(self):
|
|
67
|
+
"""Test get_implementation_status returns 'Implemented' for Pass result."""
|
|
68
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
69
|
+
self.assertEqual(item.get_implementation_status(), "Implemented")
|
|
70
|
+
|
|
71
|
+
def test_get_implementation_status_fail(self):
|
|
72
|
+
"""Test get_implementation_status returns 'In Remediation' for Fail result."""
|
|
73
|
+
csv_row = {**self.sample_csv_row, "Result": "Failed"}
|
|
74
|
+
item = WizComplianceReportItem(csv_row)
|
|
75
|
+
self.assertEqual(item.get_implementation_status(), "In Remediation")
|
|
76
|
+
|
|
77
|
+
def test_get_severity_mapping(self):
|
|
78
|
+
"""Test get_severity correctly maps Wiz severities to RegScale severities."""
|
|
79
|
+
test_cases = [
|
|
80
|
+
("CRITICAL", "High"),
|
|
81
|
+
("HIGH", "High"),
|
|
82
|
+
("MEDIUM", "Moderate"),
|
|
83
|
+
("LOW", "Low"),
|
|
84
|
+
("INFORMATIONAL", "Low"),
|
|
85
|
+
("unknown", "Low"),
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
for wiz_severity, expected_regscale_severity in test_cases:
|
|
89
|
+
csv_row = {**self.sample_csv_row, "Severity": wiz_severity}
|
|
90
|
+
item = WizComplianceReportItem(csv_row)
|
|
91
|
+
self.assertEqual(item.get_severity(), expected_regscale_severity)
|
|
92
|
+
|
|
93
|
+
def test_get_unique_resource_name_with_region(self):
|
|
94
|
+
"""Test get_unique_resource_name includes region when available."""
|
|
95
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
96
|
+
result = item.get_unique_resource_name()
|
|
97
|
+
self.assertIn("test-vm-001", result)
|
|
98
|
+
self.assertIn("East US", result)
|
|
99
|
+
|
|
100
|
+
def test_get_unique_resource_name_no_region(self):
|
|
101
|
+
"""Test get_unique_resource_name works without region."""
|
|
102
|
+
csv_row = {**self.sample_csv_row, "Resource Region": ""}
|
|
103
|
+
item = WizComplianceReportItem(csv_row)
|
|
104
|
+
result = item.get_unique_resource_name()
|
|
105
|
+
self.assertIn("test-vm-001", result)
|
|
106
|
+
|
|
107
|
+
def test_get_unique_resource_name_with_resource_id(self):
|
|
108
|
+
"""Test get_unique_resource_name includes resource ID suffix."""
|
|
109
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
110
|
+
result = item.get_unique_resource_name()
|
|
111
|
+
# Should include the last part of the resource ID
|
|
112
|
+
self.assertIn("test-vm-001", result)
|
|
113
|
+
|
|
114
|
+
def test_get_unique_resource_name_no_name(self):
|
|
115
|
+
"""Test get_unique_resource_name defaults to 'Unknown Resource' when name is empty."""
|
|
116
|
+
csv_row = {**self.sample_csv_row, "Resource Name": ""}
|
|
117
|
+
item = WizComplianceReportItem(csv_row)
|
|
118
|
+
result = item.get_unique_resource_name()
|
|
119
|
+
self.assertIn("Unknown Resource", result)
|
|
120
|
+
|
|
121
|
+
def test_get_unique_resource_name_truncates_long_ids(self):
|
|
122
|
+
"""Test get_unique_resource_name truncates long resource IDs."""
|
|
123
|
+
csv_row = {
|
|
124
|
+
**self.sample_csv_row,
|
|
125
|
+
"Resource ID": "/subscriptions/12345/resourceGroups/rg-test/providers/Microsoft.Compute/virtualMachines/very-long-resource-name-that-should-be-truncated",
|
|
126
|
+
}
|
|
127
|
+
item = WizComplianceReportItem(csv_row)
|
|
128
|
+
result = item.get_unique_resource_name()
|
|
129
|
+
# Should truncate to 12 chars
|
|
130
|
+
self.assertIn("[", result)
|
|
131
|
+
self.assertIn("]", result)
|
|
132
|
+
|
|
133
|
+
def test_get_unique_issue_identifier(self):
|
|
134
|
+
"""Test get_unique_issue_identifier creates unique identifier for deduplication."""
|
|
135
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
136
|
+
result = item.get_unique_issue_identifier()
|
|
137
|
+
self.assertIn("|", result)
|
|
138
|
+
self.assertIn("policy-disk-encryption-001", result)
|
|
139
|
+
|
|
140
|
+
def test_get_title(self):
|
|
141
|
+
"""Test get_title returns formatted title."""
|
|
142
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
143
|
+
result = item.get_title()
|
|
144
|
+
self.assertIn("AC-2(1)", result)
|
|
145
|
+
self.assertIn("Ensure VM disk encryption is enabled", result)
|
|
146
|
+
|
|
147
|
+
def test_get_description(self):
|
|
148
|
+
"""Test get_description returns formatted description."""
|
|
149
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
150
|
+
result = item.get_description()
|
|
151
|
+
self.assertIn("Wiz compliance assessment", result)
|
|
152
|
+
self.assertIn("Ensure VM disk encryption is enabled", result)
|
|
153
|
+
|
|
154
|
+
def test_get_finding_details(self):
|
|
155
|
+
"""Test get_finding_details returns formatted details."""
|
|
156
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
157
|
+
result = item.get_finding_details()
|
|
158
|
+
self.assertIn("Resource:", result)
|
|
159
|
+
self.assertIn("Cloud Provider:", result)
|
|
160
|
+
self.assertIn("Azure", result)
|
|
161
|
+
self.assertIn("Result:", result)
|
|
162
|
+
self.assertIn("Remediation:", result)
|
|
163
|
+
|
|
164
|
+
def test_get_finding_details_with_subscription(self):
|
|
165
|
+
"""Test get_finding_details includes subscription when present."""
|
|
166
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
167
|
+
result = item.get_finding_details()
|
|
168
|
+
self.assertIn("Subscription:", result)
|
|
169
|
+
self.assertIn("Dev Subscription", result)
|
|
170
|
+
|
|
171
|
+
def test_get_asset_identifier(self):
|
|
172
|
+
"""Test get_asset_identifier returns correct identifier."""
|
|
173
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
174
|
+
result = item.get_asset_identifier()
|
|
175
|
+
self.assertEqual(result, self.sample_csv_row["Cloud Provider ID"])
|
|
176
|
+
|
|
177
|
+
def test_affected_controls_single_control(self):
|
|
178
|
+
"""Test affected_controls property with single control."""
|
|
179
|
+
item = WizComplianceReportItem(self.sample_csv_row)
|
|
180
|
+
result = item.affected_controls
|
|
181
|
+
self.assertEqual(result, "AC-2(1)")
|
|
182
|
+
|
|
183
|
+
def test_affected_controls_multiple_controls(self):
|
|
184
|
+
"""Test affected_controls property with multiple controls."""
|
|
185
|
+
csv_row = {
|
|
186
|
+
**self.sample_csv_row,
|
|
187
|
+
"Compliance Check Name (Wiz Subcategory)": "AC-2(1) Account Management, AC-3 Access Enforcement",
|
|
188
|
+
}
|
|
189
|
+
item = WizComplianceReportItem(csv_row)
|
|
190
|
+
result = item.affected_controls
|
|
191
|
+
self.assertEqual(result, "AC-2(1),AC-3")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@pytest.mark.no_parallel
|
|
195
|
+
@patch("regscale.integrations.compliance_integration.ComplianceIntegration.__init__", return_value=None)
|
|
196
|
+
class TestWizComplianceReportProcessorComprehensive(unittest.TestCase):
|
|
197
|
+
"""Comprehensive test suite for WizComplianceReportProcessor class."""
|
|
198
|
+
|
|
199
|
+
def setUp(self):
|
|
200
|
+
"""Set up test fixtures."""
|
|
201
|
+
self.test_plan_id = 12345
|
|
202
|
+
self.test_project_id = "test-project-123"
|
|
203
|
+
self.test_client_id = "test-client-id"
|
|
204
|
+
self.test_client_secret = "test-client-secret"
|
|
205
|
+
|
|
206
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
207
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.WizReportManager")
|
|
208
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.ControlMatcher")
|
|
209
|
+
def test_init_successful(self, mock_control_matcher, mock_report_manager, mock_auth, mock_parent_init):
|
|
210
|
+
"""Test successful initialization of WizComplianceReportProcessor."""
|
|
211
|
+
mock_auth.return_value = "test-access-token"
|
|
212
|
+
mock_parent_init.return_value = None
|
|
213
|
+
|
|
214
|
+
processor = WizComplianceReportProcessor(
|
|
215
|
+
plan_id=self.test_plan_id,
|
|
216
|
+
wiz_project_id=self.test_project_id,
|
|
217
|
+
client_id=self.test_client_id,
|
|
218
|
+
client_secret=self.test_client_secret,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
self.assertEqual(processor.wiz_project_id, self.test_project_id)
|
|
222
|
+
mock_auth.assert_called_once_with(self.test_client_id, self.test_client_secret)
|
|
223
|
+
mock_report_manager.assert_called_once()
|
|
224
|
+
|
|
225
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
226
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.error_and_exit")
|
|
227
|
+
def test_init_authentication_failure(self, mock_error_exit, mock_auth, mock_parent_init):
|
|
228
|
+
"""Test initialization handles authentication failure."""
|
|
229
|
+
mock_auth.return_value = None
|
|
230
|
+
mock_error_exit.side_effect = SystemExit(1)
|
|
231
|
+
|
|
232
|
+
with pytest.raises(SystemExit):
|
|
233
|
+
WizComplianceReportProcessor(
|
|
234
|
+
plan_id=self.test_plan_id,
|
|
235
|
+
wiz_project_id=self.test_project_id,
|
|
236
|
+
client_id=self.test_client_id,
|
|
237
|
+
client_secret=self.test_client_secret,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
mock_error_exit.assert_called_once_with("Failed to authenticate with Wiz")
|
|
241
|
+
|
|
242
|
+
def test_parse_csv_report(self, mock_parent_init):
|
|
243
|
+
"""Test parse_csv_report successfully parses CSV file."""
|
|
244
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False) as temp_file:
|
|
245
|
+
writer = csv.DictWriter(
|
|
246
|
+
temp_file,
|
|
247
|
+
fieldnames=[
|
|
248
|
+
"Resource Name",
|
|
249
|
+
"Cloud Provider",
|
|
250
|
+
"Cloud Provider ID",
|
|
251
|
+
"Resource ID",
|
|
252
|
+
"Resource Region",
|
|
253
|
+
"Subscription",
|
|
254
|
+
"Subscription Name",
|
|
255
|
+
"Policy Name",
|
|
256
|
+
"Policy ID",
|
|
257
|
+
"Result",
|
|
258
|
+
"Severity",
|
|
259
|
+
"Compliance Check Name (Wiz Subcategory)",
|
|
260
|
+
"Framework",
|
|
261
|
+
"Remediation Steps",
|
|
262
|
+
],
|
|
263
|
+
)
|
|
264
|
+
writer.writeheader()
|
|
265
|
+
writer.writerow(
|
|
266
|
+
{
|
|
267
|
+
"Resource Name": "test-vm",
|
|
268
|
+
"Cloud Provider": "Azure",
|
|
269
|
+
"Cloud Provider ID": "test-id",
|
|
270
|
+
"Resource ID": "test-resource-id",
|
|
271
|
+
"Resource Region": "East US",
|
|
272
|
+
"Subscription": "sub-123",
|
|
273
|
+
"Subscription Name": "Test Sub",
|
|
274
|
+
"Policy Name": "Test Policy",
|
|
275
|
+
"Policy ID": "policy-123",
|
|
276
|
+
"Result": "Pass",
|
|
277
|
+
"Severity": "HIGH",
|
|
278
|
+
"Compliance Check Name (Wiz Subcategory)": "AC-2 Account Management",
|
|
279
|
+
"Framework": "NIST 800-53",
|
|
280
|
+
"Remediation Steps": "Fix it",
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
temp_file.flush()
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate") as mock_auth:
|
|
287
|
+
mock_auth.return_value = "test-token"
|
|
288
|
+
processor = WizComplianceReportProcessor(
|
|
289
|
+
plan_id=self.test_plan_id,
|
|
290
|
+
wiz_project_id=self.test_project_id,
|
|
291
|
+
client_id=self.test_client_id,
|
|
292
|
+
client_secret=self.test_client_secret,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
items = processor.parse_csv_report(temp_file.name)
|
|
296
|
+
self.assertEqual(len(items), 1)
|
|
297
|
+
self.assertIsInstance(items[0], WizComplianceReportItem)
|
|
298
|
+
self.assertEqual(items[0]._resource_name, "test-vm")
|
|
299
|
+
finally:
|
|
300
|
+
os.unlink(temp_file.name)
|
|
301
|
+
|
|
302
|
+
def test_parse_csv_report_gzipped(self, mock_parent_init):
|
|
303
|
+
"""Test parse_csv_report handles gzipped files."""
|
|
304
|
+
with tempfile.NamedTemporaryFile(suffix=".csv.gz", delete=False) as temp_file:
|
|
305
|
+
with gzip.open(temp_file.name, "wt", encoding="utf-8") as gz_file:
|
|
306
|
+
writer = csv.DictWriter(
|
|
307
|
+
gz_file,
|
|
308
|
+
fieldnames=[
|
|
309
|
+
"Resource Name",
|
|
310
|
+
"Cloud Provider",
|
|
311
|
+
"Cloud Provider ID",
|
|
312
|
+
"Resource ID",
|
|
313
|
+
"Resource Region",
|
|
314
|
+
"Subscription",
|
|
315
|
+
"Subscription Name",
|
|
316
|
+
"Policy Name",
|
|
317
|
+
"Policy ID",
|
|
318
|
+
"Result",
|
|
319
|
+
"Severity",
|
|
320
|
+
"Compliance Check Name (Wiz Subcategory)",
|
|
321
|
+
"Framework",
|
|
322
|
+
"Remediation Steps",
|
|
323
|
+
],
|
|
324
|
+
)
|
|
325
|
+
writer.writeheader()
|
|
326
|
+
writer.writerow(
|
|
327
|
+
{
|
|
328
|
+
"Resource Name": "test-vm",
|
|
329
|
+
"Cloud Provider": "Azure",
|
|
330
|
+
"Cloud Provider ID": "test-id",
|
|
331
|
+
"Resource ID": "test-resource-id",
|
|
332
|
+
"Resource Region": "East US",
|
|
333
|
+
"Subscription": "sub-123",
|
|
334
|
+
"Subscription Name": "Test Sub",
|
|
335
|
+
"Policy Name": "Test Policy",
|
|
336
|
+
"Policy ID": "policy-123",
|
|
337
|
+
"Result": "Pass",
|
|
338
|
+
"Severity": "HIGH",
|
|
339
|
+
"Compliance Check Name (Wiz Subcategory)": "AC-2 Account Management",
|
|
340
|
+
"Framework": "NIST 800-53",
|
|
341
|
+
"Remediation Steps": "Fix it",
|
|
342
|
+
}
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
try:
|
|
346
|
+
with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate") as mock_auth:
|
|
347
|
+
mock_auth.return_value = "test-token"
|
|
348
|
+
processor = WizComplianceReportProcessor(
|
|
349
|
+
plan_id=self.test_plan_id,
|
|
350
|
+
wiz_project_id=self.test_project_id,
|
|
351
|
+
client_id=self.test_client_id,
|
|
352
|
+
client_secret=self.test_client_secret,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
items = processor.parse_csv_report(temp_file.name)
|
|
356
|
+
self.assertEqual(len(items), 1)
|
|
357
|
+
self.assertIsInstance(items[0], WizComplianceReportItem)
|
|
358
|
+
finally:
|
|
359
|
+
os.unlink(temp_file.name)
|
|
360
|
+
|
|
361
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.logger")
|
|
362
|
+
def test_parse_csv_report_error_handling(self, mock_logger, mock_parent_init):
|
|
363
|
+
"""Test parse_csv_report handles errors gracefully."""
|
|
364
|
+
with patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate") as mock_auth:
|
|
365
|
+
mock_auth.return_value = "test-token"
|
|
366
|
+
processor = WizComplianceReportProcessor(
|
|
367
|
+
plan_id=self.test_plan_id,
|
|
368
|
+
wiz_project_id=self.test_project_id,
|
|
369
|
+
client_id=self.test_client_id,
|
|
370
|
+
client_secret=self.test_client_secret,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
items = processor.parse_csv_report("/nonexistent/file.csv")
|
|
374
|
+
self.assertEqual(len(items), 0)
|
|
375
|
+
mock_logger.error.assert_called()
|
|
376
|
+
|
|
377
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
378
|
+
def test_fetch_compliance_data_with_provided_file(self, mock_auth, mock_parent_init):
|
|
379
|
+
"""Test fetch_compliance_data uses provided report file path."""
|
|
380
|
+
mock_auth.return_value = "test-token"
|
|
381
|
+
|
|
382
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False) as temp_file:
|
|
383
|
+
writer = csv.DictWriter(
|
|
384
|
+
temp_file,
|
|
385
|
+
fieldnames=[
|
|
386
|
+
"Resource Name",
|
|
387
|
+
"Cloud Provider",
|
|
388
|
+
"Cloud Provider ID",
|
|
389
|
+
"Resource ID",
|
|
390
|
+
"Resource Region",
|
|
391
|
+
"Subscription",
|
|
392
|
+
"Subscription Name",
|
|
393
|
+
"Policy Name",
|
|
394
|
+
"Policy ID",
|
|
395
|
+
"Result",
|
|
396
|
+
"Severity",
|
|
397
|
+
"Compliance Check Name (Wiz Subcategory)",
|
|
398
|
+
"Framework",
|
|
399
|
+
"Remediation Steps",
|
|
400
|
+
],
|
|
401
|
+
)
|
|
402
|
+
writer.writeheader()
|
|
403
|
+
writer.writerow(
|
|
404
|
+
{
|
|
405
|
+
"Resource Name": "test-vm",
|
|
406
|
+
"Cloud Provider": "Azure",
|
|
407
|
+
"Cloud Provider ID": "test-id",
|
|
408
|
+
"Resource ID": "test-resource-id",
|
|
409
|
+
"Resource Region": "East US",
|
|
410
|
+
"Subscription": "sub-123",
|
|
411
|
+
"Subscription Name": "Test Sub",
|
|
412
|
+
"Policy Name": "Test Policy",
|
|
413
|
+
"Policy ID": "policy-123",
|
|
414
|
+
"Result": "Pass",
|
|
415
|
+
"Severity": "HIGH",
|
|
416
|
+
"Compliance Check Name (Wiz Subcategory)": "AC-2 Account Management",
|
|
417
|
+
"Framework": "NIST 800-53",
|
|
418
|
+
"Remediation Steps": "Fix it",
|
|
419
|
+
}
|
|
420
|
+
)
|
|
421
|
+
temp_file.flush()
|
|
422
|
+
|
|
423
|
+
try:
|
|
424
|
+
processor = WizComplianceReportProcessor(
|
|
425
|
+
plan_id=self.test_plan_id,
|
|
426
|
+
wiz_project_id=self.test_project_id,
|
|
427
|
+
client_id=self.test_client_id,
|
|
428
|
+
client_secret=self.test_client_secret,
|
|
429
|
+
report_file_path=temp_file.name,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
raw_data = processor.fetch_compliance_data()
|
|
433
|
+
self.assertEqual(len(raw_data), 1)
|
|
434
|
+
self.assertEqual(raw_data[0]["Resource Name"], "test-vm")
|
|
435
|
+
finally:
|
|
436
|
+
os.unlink(temp_file.name)
|
|
437
|
+
|
|
438
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
439
|
+
def test_create_compliance_item(self, mock_auth, mock_parent_init):
|
|
440
|
+
"""Test create_compliance_item creates WizComplianceReportItem."""
|
|
441
|
+
mock_auth.return_value = "test-token"
|
|
442
|
+
|
|
443
|
+
processor = WizComplianceReportProcessor(
|
|
444
|
+
plan_id=self.test_plan_id,
|
|
445
|
+
wiz_project_id=self.test_project_id,
|
|
446
|
+
client_id=self.test_client_id,
|
|
447
|
+
client_secret=self.test_client_secret,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
raw_data = {
|
|
451
|
+
"Resource Name": "test-vm",
|
|
452
|
+
"Cloud Provider": "Azure",
|
|
453
|
+
"Cloud Provider ID": "test-id",
|
|
454
|
+
"Resource ID": "test-resource-id",
|
|
455
|
+
"Resource Region": "East US",
|
|
456
|
+
"Subscription": "sub-123",
|
|
457
|
+
"Subscription Name": "Test Sub",
|
|
458
|
+
"Policy Name": "Test Policy",
|
|
459
|
+
"Policy ID": "policy-123",
|
|
460
|
+
"Result": "Pass",
|
|
461
|
+
"Severity": "HIGH",
|
|
462
|
+
"Compliance Check Name (Wiz Subcategory)": "AC-2 Account Management",
|
|
463
|
+
"Framework": "NIST 800-53",
|
|
464
|
+
"Remediation Steps": "Fix it",
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
item = processor.create_compliance_item(raw_data)
|
|
468
|
+
self.assertIsInstance(item, WizComplianceReportItem)
|
|
469
|
+
self.assertEqual(item._resource_name, "test-vm")
|
|
470
|
+
|
|
471
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
472
|
+
def test_map_string_severity_to_enum(self, mock_auth, mock_parent_init):
|
|
473
|
+
"""Test _map_string_severity_to_enum correctly maps severities."""
|
|
474
|
+
mock_auth.return_value = "test-token"
|
|
475
|
+
|
|
476
|
+
processor = WizComplianceReportProcessor(
|
|
477
|
+
plan_id=self.test_plan_id,
|
|
478
|
+
wiz_project_id=self.test_project_id,
|
|
479
|
+
client_id=self.test_client_id,
|
|
480
|
+
client_secret=self.test_client_secret,
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
test_cases = [
|
|
484
|
+
("CRITICAL", regscale_models.IssueSeverity.Critical),
|
|
485
|
+
("HIGH", regscale_models.IssueSeverity.High),
|
|
486
|
+
("MEDIUM", regscale_models.IssueSeverity.Moderate),
|
|
487
|
+
("MODERATE", regscale_models.IssueSeverity.Moderate),
|
|
488
|
+
("LOW", regscale_models.IssueSeverity.Low),
|
|
489
|
+
("INFORMATIONAL", regscale_models.IssueSeverity.Low),
|
|
490
|
+
("unknown", regscale_models.IssueSeverity.Low),
|
|
491
|
+
]
|
|
492
|
+
|
|
493
|
+
for severity_str, expected_enum in test_cases:
|
|
494
|
+
result = processor._map_string_severity_to_enum(severity_str)
|
|
495
|
+
self.assertEqual(result, expected_enum)
|
|
496
|
+
|
|
497
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
498
|
+
def test_process_compliance_data_with_bypass(self, mock_auth, mock_parent_init):
|
|
499
|
+
"""Test process_compliance_data uses bypass logic when enabled."""
|
|
500
|
+
mock_auth.return_value = "test-token"
|
|
501
|
+
|
|
502
|
+
processor = WizComplianceReportProcessor(
|
|
503
|
+
plan_id=self.test_plan_id,
|
|
504
|
+
wiz_project_id=self.test_project_id,
|
|
505
|
+
client_id=self.test_client_id,
|
|
506
|
+
client_secret=self.test_client_secret,
|
|
507
|
+
bypass_control_filtering=True,
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
with patch.object(processor, "_process_compliance_data_without_filtering") as mock_method:
|
|
511
|
+
processor.process_compliance_data()
|
|
512
|
+
mock_method.assert_called_once()
|
|
513
|
+
|
|
514
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
515
|
+
def test_reset_compliance_state(self, mock_auth, mock_parent_init):
|
|
516
|
+
"""Test _reset_compliance_state clears all state variables."""
|
|
517
|
+
mock_auth.return_value = "test-token"
|
|
518
|
+
|
|
519
|
+
processor = WizComplianceReportProcessor(
|
|
520
|
+
plan_id=self.test_plan_id,
|
|
521
|
+
wiz_project_id=self.test_project_id,
|
|
522
|
+
client_id=self.test_client_id,
|
|
523
|
+
client_secret=self.test_client_secret,
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
# Add some data
|
|
527
|
+
processor.all_compliance_items = [Mock()]
|
|
528
|
+
processor.failed_compliance_items = [Mock()]
|
|
529
|
+
processor.passing_controls = {"ac-2": Mock()}
|
|
530
|
+
processor.failing_controls = {"ac-3": Mock()}
|
|
531
|
+
processor.asset_compliance_map = defaultdict(list)
|
|
532
|
+
processor.asset_compliance_map["asset-1"] = [Mock()]
|
|
533
|
+
|
|
534
|
+
# Reset state
|
|
535
|
+
processor._reset_compliance_state()
|
|
536
|
+
|
|
537
|
+
self.assertEqual(len(processor.all_compliance_items), 0)
|
|
538
|
+
self.assertEqual(len(processor.failed_compliance_items), 0)
|
|
539
|
+
self.assertEqual(len(processor.passing_controls), 0)
|
|
540
|
+
self.assertEqual(len(processor.failing_controls), 0)
|
|
541
|
+
self.assertEqual(len(processor.asset_compliance_map), 0)
|
|
542
|
+
|
|
543
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
544
|
+
def test_is_valid_compliance_item_for_processing(self, mock_auth, mock_parent_init):
|
|
545
|
+
"""Test _is_valid_compliance_item_for_processing validates items correctly."""
|
|
546
|
+
mock_auth.return_value = "test-token"
|
|
547
|
+
|
|
548
|
+
processor = WizComplianceReportProcessor(
|
|
549
|
+
plan_id=self.test_plan_id,
|
|
550
|
+
wiz_project_id=self.test_project_id,
|
|
551
|
+
client_id=self.test_client_id,
|
|
552
|
+
client_secret=self.test_client_secret,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
# Valid item
|
|
556
|
+
valid_item = Mock()
|
|
557
|
+
valid_item.control_id = "AC-2"
|
|
558
|
+
valid_item.resource_id = "resource-123"
|
|
559
|
+
self.assertTrue(processor._is_valid_compliance_item_for_processing(valid_item))
|
|
560
|
+
|
|
561
|
+
# Missing control_id
|
|
562
|
+
invalid_item = Mock()
|
|
563
|
+
invalid_item.control_id = ""
|
|
564
|
+
invalid_item.resource_id = "resource-123"
|
|
565
|
+
self.assertFalse(processor._is_valid_compliance_item_for_processing(invalid_item))
|
|
566
|
+
|
|
567
|
+
# Missing resource_id
|
|
568
|
+
invalid_item = Mock()
|
|
569
|
+
invalid_item.control_id = "AC-2"
|
|
570
|
+
invalid_item.resource_id = ""
|
|
571
|
+
self.assertFalse(processor._is_valid_compliance_item_for_processing(invalid_item))
|
|
572
|
+
|
|
573
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
574
|
+
def test_add_compliance_item_to_collections(self, mock_auth, mock_parent_init):
|
|
575
|
+
"""Test _add_compliance_item_to_collections adds items correctly."""
|
|
576
|
+
mock_auth.return_value = "test-token"
|
|
577
|
+
|
|
578
|
+
processor = WizComplianceReportProcessor(
|
|
579
|
+
plan_id=self.test_plan_id,
|
|
580
|
+
wiz_project_id=self.test_project_id,
|
|
581
|
+
client_id=self.test_client_id,
|
|
582
|
+
client_secret=self.test_client_secret,
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
# Initialize attributes that would normally be set by parent __init__
|
|
586
|
+
processor.all_compliance_items = []
|
|
587
|
+
processor.failed_compliance_items = []
|
|
588
|
+
processor.asset_compliance_map = defaultdict(list)
|
|
589
|
+
processor.FAIL_STATUSES = ["Fail", "Failed"]
|
|
590
|
+
|
|
591
|
+
# Create a mock compliance item
|
|
592
|
+
compliance_item = Mock()
|
|
593
|
+
compliance_item.resource_id = "resource-123"
|
|
594
|
+
compliance_item.compliance_result = "Fail"
|
|
595
|
+
|
|
596
|
+
processor._add_compliance_item_to_collections(compliance_item)
|
|
597
|
+
|
|
598
|
+
self.assertEqual(len(processor.all_compliance_items), 1)
|
|
599
|
+
self.assertEqual(len(processor.failed_compliance_items), 1)
|
|
600
|
+
self.assertEqual(len(processor.asset_compliance_map["resource-123"]), 1)
|
|
601
|
+
|
|
602
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
603
|
+
def test_get_control_ids_for_item(self, mock_auth, mock_parent_init):
|
|
604
|
+
"""Test _get_control_ids_for_item extracts control IDs."""
|
|
605
|
+
mock_auth.return_value = "test-token"
|
|
606
|
+
|
|
607
|
+
processor = WizComplianceReportProcessor(
|
|
608
|
+
plan_id=self.test_plan_id,
|
|
609
|
+
wiz_project_id=self.test_project_id,
|
|
610
|
+
client_id=self.test_client_id,
|
|
611
|
+
client_secret=self.test_client_secret,
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
# Item with get_all_control_ids method
|
|
615
|
+
item_with_method = Mock()
|
|
616
|
+
item_with_method.get_all_control_ids.return_value = ["AC-2", "AC-3"]
|
|
617
|
+
result = processor._get_control_ids_for_item(item_with_method)
|
|
618
|
+
self.assertEqual(result, ["AC-2", "AC-3"])
|
|
619
|
+
|
|
620
|
+
# Item without get_all_control_ids method
|
|
621
|
+
item_without_method = Mock(spec=["control_id"])
|
|
622
|
+
item_without_method.control_id = "AC-2"
|
|
623
|
+
result = processor._get_control_ids_for_item(item_without_method)
|
|
624
|
+
self.assertEqual(result, ["AC-2"])
|
|
625
|
+
|
|
626
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
627
|
+
def test_is_compliance_item_failing(self, mock_auth, mock_parent_init):
|
|
628
|
+
"""Test _is_compliance_item_failing correctly identifies failing items."""
|
|
629
|
+
mock_auth.return_value = "test-token"
|
|
630
|
+
|
|
631
|
+
processor = WizComplianceReportProcessor(
|
|
632
|
+
plan_id=self.test_plan_id,
|
|
633
|
+
wiz_project_id=self.test_project_id,
|
|
634
|
+
client_id=self.test_client_id,
|
|
635
|
+
client_secret=self.test_client_secret,
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
processor.FAIL_STATUSES = ["Fail", "Failed", "Error"]
|
|
639
|
+
|
|
640
|
+
# Failing item
|
|
641
|
+
failing_item = Mock()
|
|
642
|
+
failing_item.compliance_result = "Fail"
|
|
643
|
+
self.assertTrue(processor._is_compliance_item_failing(failing_item))
|
|
644
|
+
|
|
645
|
+
# Passing item
|
|
646
|
+
passing_item = Mock()
|
|
647
|
+
passing_item.compliance_result = "Pass"
|
|
648
|
+
self.assertFalse(processor._is_compliance_item_failing(passing_item))
|
|
649
|
+
|
|
650
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
651
|
+
def test_remove_duplicate_items(self, mock_auth, mock_parent_init):
|
|
652
|
+
"""Test _remove_duplicate_items removes duplicates while preserving order."""
|
|
653
|
+
mock_auth.return_value = "test-token"
|
|
654
|
+
|
|
655
|
+
processor = WizComplianceReportProcessor(
|
|
656
|
+
plan_id=self.test_plan_id,
|
|
657
|
+
wiz_project_id=self.test_project_id,
|
|
658
|
+
client_id=self.test_client_id,
|
|
659
|
+
client_secret=self.test_client_secret,
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
# Create duplicate items
|
|
663
|
+
item1 = Mock()
|
|
664
|
+
item1.resource_id = "resource-1"
|
|
665
|
+
item1.control_id = "AC-2"
|
|
666
|
+
|
|
667
|
+
item2 = Mock()
|
|
668
|
+
item2.resource_id = "resource-2"
|
|
669
|
+
item2.control_id = "AC-3"
|
|
670
|
+
|
|
671
|
+
item3 = Mock()
|
|
672
|
+
item3.resource_id = "resource-1"
|
|
673
|
+
item3.control_id = "AC-2"
|
|
674
|
+
|
|
675
|
+
items = [item1, item2, item3]
|
|
676
|
+
result = processor._remove_duplicate_items(items)
|
|
677
|
+
|
|
678
|
+
self.assertEqual(len(result), 2)
|
|
679
|
+
self.assertIn(item1, result)
|
|
680
|
+
self.assertIn(item2, result)
|
|
681
|
+
|
|
682
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
683
|
+
def test_map_severity_to_priority(self, mock_auth, mock_parent_init):
|
|
684
|
+
"""Test _map_severity_to_priority maps severities to priorities."""
|
|
685
|
+
mock_auth.return_value = "test-token"
|
|
686
|
+
|
|
687
|
+
processor = WizComplianceReportProcessor(
|
|
688
|
+
plan_id=self.test_plan_id,
|
|
689
|
+
wiz_project_id=self.test_project_id,
|
|
690
|
+
client_id=self.test_client_id,
|
|
691
|
+
client_secret=self.test_client_secret,
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
# The method uses hasattr(severity, "value") to check if it's an enum
|
|
695
|
+
# When it's an enum, it converts it to string which gives the value attribute
|
|
696
|
+
# Since the enum value is complex (e.g., "0 - Critical - Critical Deficiency"),
|
|
697
|
+
# it won't match the priority map and will default to "Low"
|
|
698
|
+
# The method is designed to work with string severities like "Critical", "High", etc.
|
|
699
|
+
|
|
700
|
+
# Test with string severities (actual use case)
|
|
701
|
+
test_cases_strings = [
|
|
702
|
+
("Critical", "High"),
|
|
703
|
+
("High", "High"),
|
|
704
|
+
("Moderate", "Moderate"),
|
|
705
|
+
("Low", "Low"),
|
|
706
|
+
("Unknown", "Low"), # Default case
|
|
707
|
+
]
|
|
708
|
+
|
|
709
|
+
for severity_str, expected_priority in test_cases_strings:
|
|
710
|
+
# Create a mock object with a value attribute to simulate an enum
|
|
711
|
+
mock_severity = Mock()
|
|
712
|
+
mock_severity.value = severity_str
|
|
713
|
+
result = processor._map_severity_to_priority(mock_severity)
|
|
714
|
+
self.assertEqual(result, expected_priority)
|
|
715
|
+
|
|
716
|
+
# Test with direct string (no value attribute)
|
|
717
|
+
for severity_str, expected_priority in test_cases_strings:
|
|
718
|
+
result = processor._map_severity_to_priority(severity_str)
|
|
719
|
+
self.assertEqual(result, expected_priority)
|
|
720
|
+
|
|
721
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
722
|
+
def test_determine_highest_severity(self, mock_auth, mock_parent_init):
|
|
723
|
+
"""Test _determine_highest_severity returns highest severity."""
|
|
724
|
+
mock_auth.return_value = "test-token"
|
|
725
|
+
|
|
726
|
+
processor = WizComplianceReportProcessor(
|
|
727
|
+
plan_id=self.test_plan_id,
|
|
728
|
+
wiz_project_id=self.test_project_id,
|
|
729
|
+
client_id=self.test_client_id,
|
|
730
|
+
client_secret=self.test_client_secret,
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
# Test with CRITICAL
|
|
734
|
+
result = processor._determine_highest_severity(["LOW", "MEDIUM", "CRITICAL", "HIGH"])
|
|
735
|
+
self.assertEqual(result, "CRITICAL")
|
|
736
|
+
|
|
737
|
+
# Test with HIGH
|
|
738
|
+
result = processor._determine_highest_severity(["LOW", "MEDIUM", "HIGH"])
|
|
739
|
+
self.assertEqual(result, "HIGH")
|
|
740
|
+
|
|
741
|
+
# Test with empty list
|
|
742
|
+
result = processor._determine_highest_severity([])
|
|
743
|
+
self.assertEqual(result, "HIGH")
|
|
744
|
+
|
|
745
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
746
|
+
def test_collect_resource_information(self, mock_auth, mock_parent_init):
|
|
747
|
+
"""Test _collect_resource_information aggregates resource info."""
|
|
748
|
+
mock_auth.return_value = "test-token"
|
|
749
|
+
|
|
750
|
+
processor = WizComplianceReportProcessor(
|
|
751
|
+
plan_id=self.test_plan_id,
|
|
752
|
+
wiz_project_id=self.test_project_id,
|
|
753
|
+
client_id=self.test_client_id,
|
|
754
|
+
client_secret=self.test_client_secret,
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
# Create mock failed items
|
|
758
|
+
item1 = Mock()
|
|
759
|
+
item1.resource_name = "resource-1"
|
|
760
|
+
item1.severity = "HIGH"
|
|
761
|
+
item1.description = "Description 1"
|
|
762
|
+
|
|
763
|
+
item2 = Mock()
|
|
764
|
+
item2.resource_name = "resource-2"
|
|
765
|
+
item2.severity = "MEDIUM"
|
|
766
|
+
item2.description = "Description 2"
|
|
767
|
+
|
|
768
|
+
failed_items = [item1, item2]
|
|
769
|
+
result = processor._collect_resource_information(failed_items)
|
|
770
|
+
|
|
771
|
+
self.assertIn("resource-1", result["affected_resources"])
|
|
772
|
+
self.assertIn("resource-2", result["affected_resources"])
|
|
773
|
+
self.assertIn("HIGH", result["severities"])
|
|
774
|
+
self.assertIn("MEDIUM", result["severities"])
|
|
775
|
+
self.assertEqual(len(result["descriptions"]), 2)
|
|
776
|
+
|
|
777
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
778
|
+
def test_build_consolidated_description(self, mock_auth, mock_parent_init):
|
|
779
|
+
"""Test _build_consolidated_description formats description correctly."""
|
|
780
|
+
mock_auth.return_value = "test-token"
|
|
781
|
+
|
|
782
|
+
processor = WizComplianceReportProcessor(
|
|
783
|
+
plan_id=self.test_plan_id,
|
|
784
|
+
wiz_project_id=self.test_project_id,
|
|
785
|
+
client_id=self.test_client_id,
|
|
786
|
+
client_secret=self.test_client_secret,
|
|
787
|
+
)
|
|
788
|
+
|
|
789
|
+
resource_info = {
|
|
790
|
+
"affected_resources": {"resource-1", "resource-2"},
|
|
791
|
+
"descriptions": ["- resource-1: Description 1", "- resource-2: Description 2"],
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
result = processor._build_consolidated_description("AC-2", resource_info)
|
|
795
|
+
|
|
796
|
+
self.assertIn("Control AC-2", result)
|
|
797
|
+
self.assertIn("2 resource(s)", result)
|
|
798
|
+
self.assertIn("Description 1", result)
|
|
799
|
+
self.assertIn("Description 2", result)
|
|
800
|
+
|
|
801
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
802
|
+
def test_build_consolidated_description_truncates_long_lists(self, mock_auth, mock_parent_init):
|
|
803
|
+
"""Test _build_consolidated_description truncates long resource lists."""
|
|
804
|
+
mock_auth.return_value = "test-token"
|
|
805
|
+
|
|
806
|
+
processor = WizComplianceReportProcessor(
|
|
807
|
+
plan_id=self.test_plan_id,
|
|
808
|
+
wiz_project_id=self.test_project_id,
|
|
809
|
+
client_id=self.test_client_id,
|
|
810
|
+
client_secret=self.test_client_secret,
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
# Create 15 descriptions
|
|
814
|
+
descriptions = [f"- resource-{i}: Description {i}" for i in range(15)]
|
|
815
|
+
resource_info = {"affected_resources": set([f"resource-{i}" for i in range(15)]), "descriptions": descriptions}
|
|
816
|
+
|
|
817
|
+
result = processor._build_consolidated_description("AC-2", resource_info)
|
|
818
|
+
|
|
819
|
+
self.assertIn("15 resource(s)", result)
|
|
820
|
+
self.assertIn("and 5 more resources", result)
|
|
821
|
+
|
|
822
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
823
|
+
def test_is_control_already_processed(self, mock_auth, mock_parent_init):
|
|
824
|
+
"""Test _is_control_already_processed checks for duplicates."""
|
|
825
|
+
mock_auth.return_value = "test-token"
|
|
826
|
+
|
|
827
|
+
processor = WizComplianceReportProcessor(
|
|
828
|
+
plan_id=self.test_plan_id,
|
|
829
|
+
wiz_project_id=self.test_project_id,
|
|
830
|
+
client_id=self.test_client_id,
|
|
831
|
+
client_secret=self.test_client_secret,
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
processed_controls = {"ac-2", "ac-3"}
|
|
835
|
+
|
|
836
|
+
# Test with processed control (case insensitive)
|
|
837
|
+
self.assertTrue(processor._is_control_already_processed("AC-2", processed_controls))
|
|
838
|
+
self.assertTrue(processor._is_control_already_processed("ac-2", processed_controls))
|
|
839
|
+
|
|
840
|
+
# Test with unprocessed control
|
|
841
|
+
self.assertFalse(processor._is_control_already_processed("AC-4", processed_controls))
|
|
842
|
+
|
|
843
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
844
|
+
def test_find_recent_report_finds_recent_file(self, mock_auth, mock_parent_init):
|
|
845
|
+
"""Test _find_recent_report finds recent report file."""
|
|
846
|
+
mock_auth.return_value = "test-token"
|
|
847
|
+
|
|
848
|
+
processor = WizComplianceReportProcessor(
|
|
849
|
+
plan_id=self.test_plan_id,
|
|
850
|
+
wiz_project_id=self.test_project_id,
|
|
851
|
+
client_id=self.test_client_id,
|
|
852
|
+
client_secret=self.test_client_secret,
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
856
|
+
# Create a recent report file
|
|
857
|
+
report_file = os.path.join(temp_dir, f"compliance_report_{self.test_project_id}_20231201_120000.csv")
|
|
858
|
+
with open(report_file, "w") as f:
|
|
859
|
+
f.write("test")
|
|
860
|
+
|
|
861
|
+
with patch("regscale.integrations.commercial.wizv2.compliance_report.os.path.exists") as mock_exists:
|
|
862
|
+
with patch("regscale.integrations.commercial.wizv2.compliance_report.os.listdir") as mock_listdir:
|
|
863
|
+
mock_exists.return_value = True
|
|
864
|
+
mock_listdir.return_value = [os.path.basename(report_file)]
|
|
865
|
+
|
|
866
|
+
with patch(
|
|
867
|
+
"regscale.integrations.commercial.wizv2.compliance_report.os.path.getmtime"
|
|
868
|
+
) as mock_mtime:
|
|
869
|
+
# Set modification time to now
|
|
870
|
+
mock_mtime.return_value = datetime.now().timestamp()
|
|
871
|
+
|
|
872
|
+
result = processor._find_recent_report(max_age_hours=24)
|
|
873
|
+
self.assertIsNotNone(result)
|
|
874
|
+
|
|
875
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
876
|
+
def test_find_recent_report_no_directory(self, mock_auth, mock_parent_init):
|
|
877
|
+
"""Test _find_recent_report returns None when directory doesn't exist."""
|
|
878
|
+
mock_auth.return_value = "test-token"
|
|
879
|
+
|
|
880
|
+
processor = WizComplianceReportProcessor(
|
|
881
|
+
plan_id=self.test_plan_id,
|
|
882
|
+
wiz_project_id=self.test_project_id,
|
|
883
|
+
client_id=self.test_client_id,
|
|
884
|
+
client_secret=self.test_client_secret,
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
with patch("regscale.integrations.commercial.wizv2.compliance_report.os.path.exists") as mock_exists:
|
|
888
|
+
mock_exists.return_value = False
|
|
889
|
+
result = processor._find_recent_report()
|
|
890
|
+
self.assertIsNone(result)
|
|
891
|
+
|
|
892
|
+
@patch("regscale.integrations.commercial.wizv2.compliance_report.wiz_authenticate")
|
|
893
|
+
def test_process_compliance_sync(self, mock_auth, mock_parent_init):
|
|
894
|
+
"""Test process_compliance_sync calls sync_compliance."""
|
|
895
|
+
mock_auth.return_value = "test-token"
|
|
896
|
+
|
|
897
|
+
processor = WizComplianceReportProcessor(
|
|
898
|
+
plan_id=self.test_plan_id,
|
|
899
|
+
wiz_project_id=self.test_project_id,
|
|
900
|
+
client_id=self.test_client_id,
|
|
901
|
+
client_secret=self.test_client_secret,
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
with patch.object(processor, "sync_compliance") as mock_sync:
|
|
905
|
+
processor.process_compliance_sync()
|
|
906
|
+
mock_sync.assert_called_once()
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
if __name__ == "__main__":
|
|
910
|
+
unittest.main()
|