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,133 @@
|
|
|
1
|
+
"""Core Wiz integration modules including authentication, client, file operations, and constants."""
|
|
2
|
+
|
|
3
|
+
from regscale.integrations.commercial.wizv2.core.auth import (
|
|
4
|
+
AUTH0_URLS,
|
|
5
|
+
COGNITO_URLS,
|
|
6
|
+
generate_authentication_params,
|
|
7
|
+
get_token,
|
|
8
|
+
wiz_authenticate,
|
|
9
|
+
)
|
|
10
|
+
from regscale.integrations.commercial.wizv2.core.client import (
|
|
11
|
+
AsyncWizGraphQLClient,
|
|
12
|
+
run_async_queries,
|
|
13
|
+
)
|
|
14
|
+
from regscale.integrations.commercial.wizv2.core.file_operations import FileOperations
|
|
15
|
+
from regscale.integrations.commercial.wizv2.core.constants import (
|
|
16
|
+
ASSET_TYPE_MAPPING,
|
|
17
|
+
BEARER,
|
|
18
|
+
CHECK_INTERVAL_FOR_DOWNLOAD_REPORT,
|
|
19
|
+
CLOUD_CONFIG_FINDING_QUERY,
|
|
20
|
+
CLOUD_CONFIG_FINDINGS_FILE_PATH,
|
|
21
|
+
CONTENT_TYPE,
|
|
22
|
+
CPE_PART_TO_CATEGORY_MAPPING,
|
|
23
|
+
CREATE_REPORT_QUERY,
|
|
24
|
+
DATA_FINDING_QUERY,
|
|
25
|
+
DATA_FINDINGS_FILE_PATH,
|
|
26
|
+
DATASOURCE,
|
|
27
|
+
DEFAULT_WIZ_HARDWARE_TYPES,
|
|
28
|
+
DOWNLOAD_QUERY,
|
|
29
|
+
END_OF_LIFE_FILE_PATH,
|
|
30
|
+
END_OF_LIFE_QUERY,
|
|
31
|
+
EXCESSIVE_ACCESS_FILE_PATH,
|
|
32
|
+
EXCESSIVE_ACCESS_QUERY,
|
|
33
|
+
EXTERNAL_ATTACK_SURFACE_FILE_PATH,
|
|
34
|
+
EXTERNAL_ATTACK_SURFACE_QUERY,
|
|
35
|
+
FRAMEWORK_CATEGORIES,
|
|
36
|
+
FRAMEWORK_MAPPINGS,
|
|
37
|
+
FRAMEWORK_SHORTCUTS,
|
|
38
|
+
HOST_VULNERABILITY_FILE_PATH,
|
|
39
|
+
HOST_VULNERABILITY_QUERY,
|
|
40
|
+
INVENTORY_FILE_PATH,
|
|
41
|
+
INVENTORY_QUERY,
|
|
42
|
+
ISSUE_QUERY,
|
|
43
|
+
ISSUES_FILE_PATH,
|
|
44
|
+
MAX_RETRIES,
|
|
45
|
+
NETWORK_EXPOSURE_FILE_PATH,
|
|
46
|
+
NETWORK_EXPOSURE_QUERY,
|
|
47
|
+
PROVIDER,
|
|
48
|
+
RATE_LIMIT_MSG,
|
|
49
|
+
RECOMMENDED_WIZ_INVENTORY_TYPES,
|
|
50
|
+
REPORTS_QUERY,
|
|
51
|
+
RERUN_REPORT_QUERY,
|
|
52
|
+
RESOURCE,
|
|
53
|
+
SBOM_FILE_PATH,
|
|
54
|
+
SBOM_QUERY,
|
|
55
|
+
SECRET_FINDINGS_FILE_PATH,
|
|
56
|
+
SECRET_FINDINGS_QUERY,
|
|
57
|
+
SEVERITY_MAP,
|
|
58
|
+
TECHNOLOGIES_FILE_PATH,
|
|
59
|
+
VULNERABILITY_FILE_PATH,
|
|
60
|
+
VULNERABILITY_QUERY,
|
|
61
|
+
WIZ_FRAMEWORK_QUERY,
|
|
62
|
+
WIZ_POLICY_QUERY,
|
|
63
|
+
WizVulnerabilityType,
|
|
64
|
+
get_compliance_report_variables,
|
|
65
|
+
get_wiz_issue_queries,
|
|
66
|
+
get_wiz_vulnerability_queries,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
__all__ = [
|
|
70
|
+
# Auth
|
|
71
|
+
"AUTH0_URLS",
|
|
72
|
+
"COGNITO_URLS",
|
|
73
|
+
"generate_authentication_params",
|
|
74
|
+
"get_token",
|
|
75
|
+
"wiz_authenticate",
|
|
76
|
+
# Client
|
|
77
|
+
"AsyncWizGraphQLClient",
|
|
78
|
+
"run_async_queries",
|
|
79
|
+
# File Operations
|
|
80
|
+
"FileOperations",
|
|
81
|
+
# Constants
|
|
82
|
+
"ASSET_TYPE_MAPPING",
|
|
83
|
+
"BEARER",
|
|
84
|
+
"CHECK_INTERVAL_FOR_DOWNLOAD_REPORT",
|
|
85
|
+
"CLOUD_CONFIG_FINDING_QUERY",
|
|
86
|
+
"CLOUD_CONFIG_FINDINGS_FILE_PATH",
|
|
87
|
+
"CONTENT_TYPE",
|
|
88
|
+
"CPE_PART_TO_CATEGORY_MAPPING",
|
|
89
|
+
"CREATE_REPORT_QUERY",
|
|
90
|
+
"DATA_FINDING_QUERY",
|
|
91
|
+
"DATA_FINDINGS_FILE_PATH",
|
|
92
|
+
"DATASOURCE",
|
|
93
|
+
"DEFAULT_WIZ_HARDWARE_TYPES",
|
|
94
|
+
"DOWNLOAD_QUERY",
|
|
95
|
+
"END_OF_LIFE_FILE_PATH",
|
|
96
|
+
"END_OF_LIFE_QUERY",
|
|
97
|
+
"EXCESSIVE_ACCESS_FILE_PATH",
|
|
98
|
+
"EXCESSIVE_ACCESS_QUERY",
|
|
99
|
+
"EXTERNAL_ATTACK_SURFACE_FILE_PATH",
|
|
100
|
+
"EXTERNAL_ATTACK_SURFACE_QUERY",
|
|
101
|
+
"FRAMEWORK_CATEGORIES",
|
|
102
|
+
"FRAMEWORK_MAPPINGS",
|
|
103
|
+
"FRAMEWORK_SHORTCUTS",
|
|
104
|
+
"HOST_VULNERABILITY_FILE_PATH",
|
|
105
|
+
"HOST_VULNERABILITY_QUERY",
|
|
106
|
+
"INVENTORY_FILE_PATH",
|
|
107
|
+
"INVENTORY_QUERY",
|
|
108
|
+
"ISSUE_QUERY",
|
|
109
|
+
"ISSUES_FILE_PATH",
|
|
110
|
+
"MAX_RETRIES",
|
|
111
|
+
"NETWORK_EXPOSURE_FILE_PATH",
|
|
112
|
+
"NETWORK_EXPOSURE_QUERY",
|
|
113
|
+
"PROVIDER",
|
|
114
|
+
"RATE_LIMIT_MSG",
|
|
115
|
+
"RECOMMENDED_WIZ_INVENTORY_TYPES",
|
|
116
|
+
"REPORTS_QUERY",
|
|
117
|
+
"RERUN_REPORT_QUERY",
|
|
118
|
+
"RESOURCE",
|
|
119
|
+
"SBOM_FILE_PATH",
|
|
120
|
+
"SBOM_QUERY",
|
|
121
|
+
"SECRET_FINDINGS_FILE_PATH",
|
|
122
|
+
"SECRET_FINDINGS_QUERY",
|
|
123
|
+
"SEVERITY_MAP",
|
|
124
|
+
"TECHNOLOGIES_FILE_PATH",
|
|
125
|
+
"VULNERABILITY_FILE_PATH",
|
|
126
|
+
"VULNERABILITY_QUERY",
|
|
127
|
+
"WIZ_FRAMEWORK_QUERY",
|
|
128
|
+
"WIZ_POLICY_QUERY",
|
|
129
|
+
"WizVulnerabilityType",
|
|
130
|
+
"get_compliance_report_variables",
|
|
131
|
+
"get_wiz_issue_queries",
|
|
132
|
+
"get_wiz_vulnerability_queries",
|
|
133
|
+
]
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
7
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
8
8
|
|
|
9
9
|
import anyio
|
|
10
10
|
import httpx
|
|
@@ -48,7 +48,7 @@ class AsyncWizGraphQLClient:
|
|
|
48
48
|
self,
|
|
49
49
|
query: str,
|
|
50
50
|
variables: Optional[Dict[str, Any]] = None,
|
|
51
|
-
progress_callback: Optional[
|
|
51
|
+
progress_callback: Optional[Callable] = None,
|
|
52
52
|
task_name: str = "GraphQL Query",
|
|
53
53
|
) -> Dict[str, Any]:
|
|
54
54
|
"""
|
|
@@ -118,7 +118,7 @@ class AsyncWizGraphQLClient:
|
|
|
118
118
|
query: str,
|
|
119
119
|
variables: Dict[str, Any],
|
|
120
120
|
topic_key: str,
|
|
121
|
-
progress_callback: Optional[
|
|
121
|
+
progress_callback: Optional[Callable] = None,
|
|
122
122
|
task_name: str = "Paginated Query",
|
|
123
123
|
) -> List[Dict[str, Any]]:
|
|
124
124
|
"""
|
|
@@ -156,6 +156,10 @@ class AsyncWizGraphQLClient:
|
|
|
156
156
|
nodes = topic_data.get("nodes", [])
|
|
157
157
|
page_info = topic_data.get("pageInfo", {})
|
|
158
158
|
|
|
159
|
+
# Handle case where nodes is explicitly None
|
|
160
|
+
if nodes is None:
|
|
161
|
+
nodes = []
|
|
162
|
+
|
|
159
163
|
all_nodes.extend(nodes)
|
|
160
164
|
|
|
161
165
|
has_next_page = page_info.get("hasNextPage", False)
|
|
@@ -545,6 +545,76 @@ CREATE_REPORT_QUERY = """
|
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
547
|
"""
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def get_compliance_report_variables(
|
|
551
|
+
project_id: str, run_starts_at: Optional[str] = None, framework_ids: Optional[List[str]] = None
|
|
552
|
+
) -> dict:
|
|
553
|
+
"""Get compliance report variables with dynamic projectId and runStartsAt.
|
|
554
|
+
|
|
555
|
+
:param str project_id: The Wiz project ID
|
|
556
|
+
:param Optional[str] run_starts_at: ISO timestamp for when the report should start, defaults to current time
|
|
557
|
+
:param Optional[List[str]] framework_ids: List of framework IDs to include, defaults to NIST SP 800-53 Rev 5
|
|
558
|
+
:return: Variables for compliance report creation
|
|
559
|
+
:rtype: dict
|
|
560
|
+
"""
|
|
561
|
+
from datetime import datetime, timezone
|
|
562
|
+
|
|
563
|
+
if not run_starts_at:
|
|
564
|
+
run_starts_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
|
565
|
+
|
|
566
|
+
if not framework_ids:
|
|
567
|
+
# Default to NIST SP 800-53 Revision 5
|
|
568
|
+
framework_ids = ["wf-id-4"]
|
|
569
|
+
|
|
570
|
+
return {
|
|
571
|
+
"input": {
|
|
572
|
+
"name": f"Compliance Report - {project_id}",
|
|
573
|
+
"type": "COMPLIANCE_ASSESSMENTS",
|
|
574
|
+
"compressionMethod": "GZIP",
|
|
575
|
+
"runIntervalHours": 168,
|
|
576
|
+
"runStartsAt": run_starts_at,
|
|
577
|
+
"csvDelimiter": "US",
|
|
578
|
+
"projectId": project_id,
|
|
579
|
+
"complianceAssessmentsParams": {
|
|
580
|
+
"securityFrameworkIds": framework_ids,
|
|
581
|
+
},
|
|
582
|
+
"emailTargetParams": None,
|
|
583
|
+
"exportDestinations": None,
|
|
584
|
+
"columnSelection": [
|
|
585
|
+
"Assessed At",
|
|
586
|
+
"Category",
|
|
587
|
+
"Cloud Provider",
|
|
588
|
+
"Cloud Provider ID",
|
|
589
|
+
"Compliance Check Name (Wiz Subcategory)",
|
|
590
|
+
"Created At",
|
|
591
|
+
"Framework",
|
|
592
|
+
"Ignore Reason",
|
|
593
|
+
"Issue/Finding ID",
|
|
594
|
+
"Native Type",
|
|
595
|
+
"Object Type",
|
|
596
|
+
"Policy Description",
|
|
597
|
+
"Policy ID",
|
|
598
|
+
"Policy Name",
|
|
599
|
+
"Policy Short Name",
|
|
600
|
+
"Policy Type",
|
|
601
|
+
"Projects",
|
|
602
|
+
"Remediation Steps",
|
|
603
|
+
"Resource Cloud Platform",
|
|
604
|
+
"Resource Group Name",
|
|
605
|
+
"Resource ID",
|
|
606
|
+
"Resource Name",
|
|
607
|
+
"Resource Region",
|
|
608
|
+
"Result",
|
|
609
|
+
"Severity",
|
|
610
|
+
"Subscription",
|
|
611
|
+
"Subscription Name",
|
|
612
|
+
"Subscription Provider ID",
|
|
613
|
+
],
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
|
|
548
618
|
REPORTS_QUERY = """
|
|
549
619
|
query ReportsTable($filterBy: ReportFilters, $first: Int, $after: String) {
|
|
550
620
|
reports(first: $first, after: $after, filterBy: $filterBy) {
|
|
@@ -562,22 +632,6 @@ REPORTS_QUERY = """
|
|
|
562
632
|
emailTarget {
|
|
563
633
|
to
|
|
564
634
|
}
|
|
565
|
-
parameters {
|
|
566
|
-
query
|
|
567
|
-
framework {
|
|
568
|
-
name
|
|
569
|
-
}
|
|
570
|
-
subscriptions {
|
|
571
|
-
id
|
|
572
|
-
name
|
|
573
|
-
type
|
|
574
|
-
}
|
|
575
|
-
entities {
|
|
576
|
-
id
|
|
577
|
-
name
|
|
578
|
-
type
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
635
|
lastRun {
|
|
582
636
|
...LastRunDetails
|
|
583
637
|
}
|
|
@@ -1101,85 +1155,24 @@ fragment SecuritySubCategoryDetails on SecuritySubCategory {
|
|
|
1101
1155
|
}
|
|
1102
1156
|
"""
|
|
1103
1157
|
DATA_FINDING_QUERY = """
|
|
1104
|
-
query
|
|
1105
|
-
|
|
1106
|
-
groupBy: $groupBy
|
|
1158
|
+
query DataFindingsTable($after: String, $first: Int, $filterBy: DataFindingFiltersV2, $orderBy: DataFindingOrder, $fetchTotalCount: Boolean = true) {
|
|
1159
|
+
dataFindingsV2(
|
|
1107
1160
|
filterBy: $filterBy
|
|
1108
1161
|
first: $first
|
|
1109
1162
|
after: $after
|
|
1110
1163
|
orderBy: $orderBy
|
|
1111
1164
|
) {
|
|
1112
1165
|
nodes {
|
|
1113
|
-
|
|
1114
|
-
location {
|
|
1115
|
-
countryCode
|
|
1116
|
-
state
|
|
1117
|
-
}
|
|
1118
|
-
regionCount
|
|
1119
|
-
graphEntityCount
|
|
1120
|
-
graphEntity {
|
|
1121
|
-
id
|
|
1122
|
-
name
|
|
1123
|
-
type
|
|
1124
|
-
properties
|
|
1125
|
-
projects {
|
|
1126
|
-
id
|
|
1127
|
-
name
|
|
1128
|
-
slug
|
|
1129
|
-
isFolder
|
|
1130
|
-
}
|
|
1131
|
-
issues(filterBy: {status: [OPEN, IN_PROGRESS]}) {
|
|
1132
|
-
criticalSeverityCount
|
|
1133
|
-
highSeverityCount
|
|
1134
|
-
mediumSeverityCount
|
|
1135
|
-
lowSeverityCount
|
|
1136
|
-
informationalSeverityCount
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
cloudAccount {
|
|
1140
|
-
id
|
|
1141
|
-
name
|
|
1142
|
-
externalId
|
|
1143
|
-
cloudProvider
|
|
1144
|
-
}
|
|
1145
|
-
dataClassifiers {
|
|
1146
|
-
id
|
|
1147
|
-
name
|
|
1148
|
-
category
|
|
1149
|
-
matcherType
|
|
1150
|
-
severity
|
|
1151
|
-
}
|
|
1152
|
-
securitySubCategories {
|
|
1153
|
-
id
|
|
1154
|
-
title
|
|
1155
|
-
externalId
|
|
1156
|
-
description
|
|
1157
|
-
category {
|
|
1158
|
-
id
|
|
1159
|
-
name
|
|
1160
|
-
description
|
|
1161
|
-
framework {
|
|
1162
|
-
id
|
|
1163
|
-
name
|
|
1164
|
-
description
|
|
1165
|
-
enabled
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
findingsCount
|
|
1170
|
-
dataFindings(first: 5) {
|
|
1171
|
-
nodes {
|
|
1172
|
-
...DataFindingDetails
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1166
|
+
...DataFindingDetails
|
|
1175
1167
|
}
|
|
1176
1168
|
pageInfo {
|
|
1177
1169
|
hasNextPage
|
|
1178
1170
|
endCursor
|
|
1179
1171
|
}
|
|
1180
|
-
totalCount
|
|
1172
|
+
totalCount @include(if: $fetchTotalCount)
|
|
1181
1173
|
}
|
|
1182
1174
|
}
|
|
1175
|
+
|
|
1183
1176
|
fragment DataFindingDetails on DataFinding {
|
|
1184
1177
|
id
|
|
1185
1178
|
name
|
|
@@ -1187,10 +1180,10 @@ fragment DataFindingDetails on DataFinding {
|
|
|
1187
1180
|
id
|
|
1188
1181
|
name
|
|
1189
1182
|
category
|
|
1183
|
+
isTenantSpecific
|
|
1190
1184
|
securitySubCategories {
|
|
1191
1185
|
id
|
|
1192
1186
|
title
|
|
1193
|
-
externalId
|
|
1194
1187
|
description
|
|
1195
1188
|
category {
|
|
1196
1189
|
id
|
|
@@ -1216,8 +1209,12 @@ fragment DataFindingDetails on DataFinding {
|
|
|
1216
1209
|
state
|
|
1217
1210
|
}
|
|
1218
1211
|
severity
|
|
1212
|
+
status
|
|
1219
1213
|
totalMatchCount
|
|
1220
1214
|
uniqueMatchCount
|
|
1215
|
+
maxUniqueMatchesReached
|
|
1216
|
+
uniqueLocationsCount
|
|
1217
|
+
isEntityPublic
|
|
1221
1218
|
graphEntity {
|
|
1222
1219
|
id
|
|
1223
1220
|
name
|
|
@@ -1231,6 +1228,12 @@ fragment DataFindingDetails on DataFinding {
|
|
|
1231
1228
|
}
|
|
1232
1229
|
}
|
|
1233
1230
|
externalSource
|
|
1231
|
+
details {
|
|
1232
|
+
applicationServices {
|
|
1233
|
+
id
|
|
1234
|
+
displayName
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1234
1237
|
}
|
|
1235
1238
|
"""
|
|
1236
1239
|
|
|
@@ -1295,8 +1298,8 @@ def get_wiz_vulnerability_queries(project_id: str, filter_by: Optional[dict] = N
|
|
|
1295
1298
|
"first": 200,
|
|
1296
1299
|
"quick": True,
|
|
1297
1300
|
"filterBy": {
|
|
1298
|
-
"
|
|
1299
|
-
"
|
|
1301
|
+
"resource": {"projectId": project_id},
|
|
1302
|
+
"status": ["OPEN", "IN_PROGRESS"],
|
|
1300
1303
|
},
|
|
1301
1304
|
},
|
|
1302
1305
|
},
|
|
@@ -1317,14 +1320,14 @@ def get_wiz_vulnerability_queries(project_id: str, filter_by: Optional[dict] = N
|
|
|
1317
1320
|
{
|
|
1318
1321
|
"type": WizVulnerabilityType.DATA_FINDING,
|
|
1319
1322
|
"query": DATA_FINDING_QUERY,
|
|
1320
|
-
"topic_key": "
|
|
1323
|
+
"topic_key": "dataFindingsV2",
|
|
1321
1324
|
"file_path": DATA_FINDINGS_FILE_PATH,
|
|
1322
|
-
"asset_lookup": "
|
|
1325
|
+
"asset_lookup": "graphEntity",
|
|
1323
1326
|
"variables": {
|
|
1324
1327
|
"first": 200,
|
|
1328
|
+
"fetchTotalCount": True,
|
|
1325
1329
|
"filterBy": {"projectId": [project_id]},
|
|
1326
|
-
"orderBy": {"field": "
|
|
1327
|
-
"groupBy": "GRAPH_ENTITY",
|
|
1330
|
+
"orderBy": {"field": "TOTAL_MATCHES", "direction": "DESC"},
|
|
1328
1331
|
},
|
|
1329
1332
|
},
|
|
1330
1333
|
{
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""File operations module for Wiz integration - handles caching and file I/O."""
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
from regscale.core.app.utils.app_utils import check_file_path
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger("regscale")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FileOperations:
|
|
17
|
+
"""Handles file operations for Wiz integration including caching and data persistence."""
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def load_json_file(file_path: str) -> Optional[Any]:
|
|
21
|
+
"""
|
|
22
|
+
Load data from a JSON file.
|
|
23
|
+
|
|
24
|
+
:param str file_path: Path to JSON file
|
|
25
|
+
:return: Loaded data or None if file doesn't exist or is invalid
|
|
26
|
+
:rtype: Optional[Any]
|
|
27
|
+
"""
|
|
28
|
+
if not os.path.exists(file_path):
|
|
29
|
+
logger.debug(f"File does not exist: {file_path}")
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
with open(file_path, encoding="utf-8") as f:
|
|
34
|
+
return json.load(f)
|
|
35
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
36
|
+
logger.error(f"Error reading JSON file {file_path}: {e}")
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def save_json_file(data: Any, file_path: str, create_dir: bool = True) -> bool:
|
|
41
|
+
"""
|
|
42
|
+
Save data to a JSON file.
|
|
43
|
+
|
|
44
|
+
:param Any data: Data to save
|
|
45
|
+
:param str file_path: Path to save file
|
|
46
|
+
:param bool create_dir: Whether to create parent directory if needed
|
|
47
|
+
:return: True if successful, False otherwise
|
|
48
|
+
:rtype: bool
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
if create_dir:
|
|
52
|
+
check_file_path(os.path.dirname(file_path))
|
|
53
|
+
|
|
54
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
55
|
+
json.dump(data, f)
|
|
56
|
+
|
|
57
|
+
logger.debug(f"Saved data to {file_path}")
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.warning(f"Failed to save data to {file_path}: {e}")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def get_file_age(file_path: str) -> Optional[datetime.timedelta]:
|
|
66
|
+
"""
|
|
67
|
+
Get the age of a file as a timedelta.
|
|
68
|
+
|
|
69
|
+
:param str file_path: Path to file
|
|
70
|
+
:return: File age or None if file doesn't exist
|
|
71
|
+
:rtype: Optional[datetime.timedelta]
|
|
72
|
+
"""
|
|
73
|
+
if not os.path.exists(file_path):
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
file_mod_time = datetime.datetime.fromtimestamp(os.path.getmtime(file_path))
|
|
78
|
+
current_time = datetime.datetime.now()
|
|
79
|
+
return current_time - file_mod_time
|
|
80
|
+
except OSError as e:
|
|
81
|
+
logger.warning(f"Error getting file age for {file_path}: {e}")
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def is_cache_valid(file_path: str, max_age_hours: float = 8) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
Check if a cache file is valid (exists and not too old).
|
|
88
|
+
|
|
89
|
+
:param str file_path: Path to cache file
|
|
90
|
+
:param float max_age_hours: Maximum age in hours before cache is invalid
|
|
91
|
+
:return: True if cache is valid, False otherwise
|
|
92
|
+
:rtype: bool
|
|
93
|
+
"""
|
|
94
|
+
file_age = FileOperations.get_file_age(file_path)
|
|
95
|
+
if file_age is None:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
max_age = datetime.timedelta(hours=max_age_hours)
|
|
99
|
+
return file_age < max_age
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def load_cache_or_fetch(
|
|
103
|
+
file_path: str,
|
|
104
|
+
fetch_fn: Callable[[], Any],
|
|
105
|
+
max_age_hours: float = 8,
|
|
106
|
+
save_cache: bool = True,
|
|
107
|
+
) -> Any:
|
|
108
|
+
"""
|
|
109
|
+
Load data from cache if valid, otherwise fetch and optionally cache.
|
|
110
|
+
|
|
111
|
+
:param str file_path: Path to cache file
|
|
112
|
+
:param Callable fetch_fn: Function to call to fetch fresh data
|
|
113
|
+
:param float max_age_hours: Maximum cache age in hours
|
|
114
|
+
:param bool save_cache: Whether to save fetched data to cache
|
|
115
|
+
:return: Data from cache or freshly fetched
|
|
116
|
+
:rtype: Any
|
|
117
|
+
"""
|
|
118
|
+
# Try to load from cache if valid
|
|
119
|
+
if FileOperations.is_cache_valid(file_path, max_age_hours):
|
|
120
|
+
logger.info(f"Using cached data from {file_path} (newer than {max_age_hours} hours)")
|
|
121
|
+
cached_data = FileOperations.load_json_file(file_path)
|
|
122
|
+
if cached_data is not None:
|
|
123
|
+
return cached_data
|
|
124
|
+
|
|
125
|
+
# Cache invalid or doesn't exist - fetch fresh data
|
|
126
|
+
file_age = FileOperations.get_file_age(file_path)
|
|
127
|
+
if file_age:
|
|
128
|
+
logger.info(
|
|
129
|
+
f"Cache file {file_path} is {file_age.total_seconds() / 3600:.1f} hours old - fetching new data"
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
logger.info(f"Cache file {file_path} does not exist - fetching new data")
|
|
133
|
+
|
|
134
|
+
data = fetch_fn()
|
|
135
|
+
|
|
136
|
+
# Save to cache if requested
|
|
137
|
+
if save_cache:
|
|
138
|
+
FileOperations.save_json_file(data, file_path)
|
|
139
|
+
|
|
140
|
+
return data
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def search_json_files(
|
|
144
|
+
identifier: str,
|
|
145
|
+
file_paths: List[str],
|
|
146
|
+
match_fn: Callable[[Dict, str], bool],
|
|
147
|
+
) -> Tuple[Optional[Dict], Optional[str]]:
|
|
148
|
+
"""
|
|
149
|
+
Search for an item across multiple JSON files.
|
|
150
|
+
|
|
151
|
+
:param str identifier: Identifier to search for
|
|
152
|
+
:param List[str] file_paths: List of file paths to search
|
|
153
|
+
:param Callable match_fn: Function to determine if an item matches (takes item and identifier)
|
|
154
|
+
:return: Tuple of (found_item, source_file) or (None, None)
|
|
155
|
+
:rtype: Tuple[Optional[Dict], Optional[str]]
|
|
156
|
+
"""
|
|
157
|
+
for file_path in file_paths:
|
|
158
|
+
if not os.path.exists(file_path):
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
result = FileOperations.search_single_json_file(identifier, file_path, match_fn)
|
|
163
|
+
if result:
|
|
164
|
+
return result, file_path
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.debug(f"Error searching {file_path}: {e}")
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
return None, None
|
|
170
|
+
|
|
171
|
+
@staticmethod
|
|
172
|
+
def search_single_json_file(
|
|
173
|
+
identifier: str,
|
|
174
|
+
file_path: str,
|
|
175
|
+
match_fn: Callable[[Dict, str], bool],
|
|
176
|
+
) -> Optional[Dict]:
|
|
177
|
+
"""
|
|
178
|
+
Search for an item in a single JSON file.
|
|
179
|
+
|
|
180
|
+
:param str identifier: Identifier to search for
|
|
181
|
+
:param str file_path: Path to JSON file
|
|
182
|
+
:param Callable match_fn: Function to determine if an item matches
|
|
183
|
+
:return: Matched item or None
|
|
184
|
+
:rtype: Optional[Dict]
|
|
185
|
+
"""
|
|
186
|
+
logger.debug(f"Searching for {identifier} in {file_path}")
|
|
187
|
+
|
|
188
|
+
data = FileOperations.load_json_file(file_path)
|
|
189
|
+
if not isinstance(data, list):
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
# Use generator for memory efficiency
|
|
193
|
+
return next((item for item in data if match_fn(item, identifier)), None)
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def load_cached_findings(
|
|
197
|
+
query_configs: List[Dict[str, Any]],
|
|
198
|
+
progress_callback: Optional[Callable] = None,
|
|
199
|
+
) -> List[Tuple[str, List[Dict], Optional[Exception]]]:
|
|
200
|
+
"""
|
|
201
|
+
Load cached findings from multiple files.
|
|
202
|
+
|
|
203
|
+
:param List[Dict[str, Any]] query_configs: Query configurations with file paths
|
|
204
|
+
:param Optional[Callable] progress_callback: Optional progress callback
|
|
205
|
+
:return: List of (query_type, nodes, error) tuples
|
|
206
|
+
:rtype: List[Tuple[str, List[Dict], Optional[Exception]]]
|
|
207
|
+
"""
|
|
208
|
+
results = []
|
|
209
|
+
|
|
210
|
+
for config in query_configs:
|
|
211
|
+
query_type = config["type"].value
|
|
212
|
+
file_path = config.get("file_path")
|
|
213
|
+
|
|
214
|
+
if progress_callback:
|
|
215
|
+
progress_callback(query_type, "loading")
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
if file_path and os.path.exists(file_path):
|
|
219
|
+
nodes = FileOperations.load_json_file(file_path)
|
|
220
|
+
if nodes is not None:
|
|
221
|
+
logger.info(f"Loaded {len(nodes)} cached {query_type} findings from {file_path}")
|
|
222
|
+
results.append((query_type, nodes, None))
|
|
223
|
+
else:
|
|
224
|
+
logger.warning(f"Failed to load cached data for {query_type}")
|
|
225
|
+
results.append((query_type, [], Exception(f"Failed to load {file_path}")))
|
|
226
|
+
else:
|
|
227
|
+
logger.warning(f"No cached data found for {query_type} at {file_path}")
|
|
228
|
+
results.append((query_type, [], None))
|
|
229
|
+
|
|
230
|
+
except Exception as e:
|
|
231
|
+
logger.error(f"Error loading cached data for {query_type}: {e}")
|
|
232
|
+
results.append((query_type, [], e))
|
|
233
|
+
|
|
234
|
+
if progress_callback:
|
|
235
|
+
progress_callback(query_type, "loaded")
|
|
236
|
+
|
|
237
|
+
return results
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Fetchers for Wiz integration - handles data retrieval and caching."""
|
|
2
|
+
|
|
3
|
+
from regscale.integrations.commercial.wizv2.fetchers.policy_assessment import (
|
|
4
|
+
PolicyAssessmentFetcher,
|
|
5
|
+
WizDataCache,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"PolicyAssessmentFetcher",
|
|
10
|
+
"WizDataCache",
|
|
11
|
+
]
|