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,198 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""AWS Config Conformance Pack to Control Mappings."""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
from typing import Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger("regscale")
|
|
10
|
+
|
|
11
|
+
# Control ID constants
|
|
12
|
+
CONTROL_IA_2_1 = "IA-2(1)"
|
|
13
|
+
|
|
14
|
+
# NIST 800-53 R5 Conformance Pack Control Mappings
|
|
15
|
+
# Maps AWS Config rule names to NIST 800-53 R5 control IDs
|
|
16
|
+
NIST_80053_R5_MAPPINGS = {
|
|
17
|
+
# Access Control (AC) Family
|
|
18
|
+
"iam-password-policy": ["AC-2", "IA-5"],
|
|
19
|
+
"iam-user-mfa-enabled": ["AC-2", CONTROL_IA_2_1],
|
|
20
|
+
"iam-root-access-key-check": ["AC-2", "AC-6"],
|
|
21
|
+
"iam-user-unused-credentials-check": ["AC-2"],
|
|
22
|
+
"iam-user-group-membership-check": ["AC-2"],
|
|
23
|
+
"iam-policy-no-statements-with-admin-access": ["AC-6"],
|
|
24
|
+
"iam-policy-no-statements-with-full-access": ["AC-6"],
|
|
25
|
+
"s3-bucket-public-read-prohibited": ["AC-3", "AC-4"],
|
|
26
|
+
"s3-bucket-public-write-prohibited": ["AC-3", "AC-4"],
|
|
27
|
+
"ec2-security-group-attached-to-eni": ["AC-4"],
|
|
28
|
+
"restricted-ssh": ["AC-4", "AC-17"],
|
|
29
|
+
"restricted-common-ports": ["AC-4"],
|
|
30
|
+
# Audit and Accountability (AU) Family
|
|
31
|
+
"cloudtrail-enabled": ["AU-2", "AU-3", "AU-6", "AU-12"],
|
|
32
|
+
"cloud-trail-cloud-watch-logs-enabled": ["AU-6"],
|
|
33
|
+
"cloudtrail-log-file-validation-enabled": ["AU-9"],
|
|
34
|
+
"cloudtrail-encryption-enabled": ["AU-9"],
|
|
35
|
+
"cloudtrail-s3-dataevents-enabled": ["AU-2"],
|
|
36
|
+
"multi-region-cloudtrail-enabled": ["AU-2"],
|
|
37
|
+
"s3-bucket-logging-enabled": ["AU-2"],
|
|
38
|
+
"rds-logging-enabled": ["AU-2"],
|
|
39
|
+
"elb-logging-enabled": ["AU-2"],
|
|
40
|
+
"cloudwatch-alarm-action-check": ["AU-6"],
|
|
41
|
+
# Configuration Management (CM) Family
|
|
42
|
+
"ec2-instance-managed-by-systems-manager": ["CM-2", "CM-6"],
|
|
43
|
+
"ec2-managedinstance-patch-compliance-status-check": ["CM-6", "SI-2"], # Maps to both CM and SI families
|
|
44
|
+
"approved-amis-by-tag": ["CM-2"],
|
|
45
|
+
# Identification and Authentication (IA) Family
|
|
46
|
+
"mfa-enabled-for-iam-console-access": [CONTROL_IA_2_1],
|
|
47
|
+
"root-account-mfa-enabled": [CONTROL_IA_2_1],
|
|
48
|
+
# System and Communications Protection (SC) Family
|
|
49
|
+
"s3-bucket-ssl-requests-only": ["SC-8", "SC-13"],
|
|
50
|
+
"alb-http-to-https-redirection-check": ["SC-8"],
|
|
51
|
+
"elb-tls-https-listeners-only": ["SC-8"],
|
|
52
|
+
"rds-snapshot-encrypted": ["SC-13"],
|
|
53
|
+
"encrypted-volumes": ["SC-13"],
|
|
54
|
+
"s3-bucket-server-side-encryption-enabled": ["SC-13"],
|
|
55
|
+
"ec2-ebs-encryption-by-default": ["SC-13"],
|
|
56
|
+
"rds-storage-encrypted": ["SC-13"],
|
|
57
|
+
"dynamodb-table-encrypted-kms": ["SC-13"],
|
|
58
|
+
# System and Information Integrity (SI) Family
|
|
59
|
+
"guardduty-enabled-centralized": ["SI-4"],
|
|
60
|
+
"securityhub-enabled": ["SI-4"],
|
|
61
|
+
"access-keys-rotated": ["SI-4"],
|
|
62
|
+
"vpc-flow-logs-enabled": ["SI-4"],
|
|
63
|
+
# Risk Assessment (RA) Family
|
|
64
|
+
"security-account-information-provided": ["RA-5"],
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def extract_control_ids_from_rule_name(rule_name: str) -> List[str]:
|
|
69
|
+
"""
|
|
70
|
+
Extract control IDs from AWS Config rule name using pattern matching.
|
|
71
|
+
|
|
72
|
+
Supports patterns like:
|
|
73
|
+
- "ac-2-iam-user-mfa-enabled"
|
|
74
|
+
- "nist-800-53-r5-ac-2"
|
|
75
|
+
- "iam-password-policy-ac-2-ia-5"
|
|
76
|
+
|
|
77
|
+
:param str rule_name: Config rule name
|
|
78
|
+
:return: List of extracted control IDs
|
|
79
|
+
:rtype: List[str]
|
|
80
|
+
"""
|
|
81
|
+
control_ids = []
|
|
82
|
+
|
|
83
|
+
# Pattern for NIST control IDs: AC-2, SI-3(1), etc.
|
|
84
|
+
pattern = r"\b([A-Z]{2}-\d+(?:\(\d+\))?)\b"
|
|
85
|
+
|
|
86
|
+
matches = re.findall(pattern, rule_name.upper())
|
|
87
|
+
control_ids.extend(matches)
|
|
88
|
+
|
|
89
|
+
return list(set(control_ids)) # Remove duplicates
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def extract_control_ids_from_tags(tags: Dict[str, str]) -> List[str]:
|
|
93
|
+
"""
|
|
94
|
+
Extract control IDs from AWS Config rule tags.
|
|
95
|
+
|
|
96
|
+
Expected tag format:
|
|
97
|
+
- ControlID=AC-2
|
|
98
|
+
- ControlID=AC-2,AU-3,SI-2
|
|
99
|
+
- ControlIDs=AC-2,AU-3
|
|
100
|
+
|
|
101
|
+
:param Dict[str, str] tags: Dictionary of tag key-value pairs
|
|
102
|
+
:return: List of extracted control IDs
|
|
103
|
+
:rtype: List[str]
|
|
104
|
+
"""
|
|
105
|
+
control_ids = []
|
|
106
|
+
|
|
107
|
+
# Check for ControlID or ControlIDs tags
|
|
108
|
+
for tag_key in ["ControlID", "ControlIDs", "Control-ID", "Control-IDs"]:
|
|
109
|
+
if tag_key in tags:
|
|
110
|
+
tag_value = tags[tag_key]
|
|
111
|
+
# Split by comma and clean up
|
|
112
|
+
ids = [cid.strip().upper() for cid in tag_value.split(",") if cid.strip()]
|
|
113
|
+
control_ids.extend(ids)
|
|
114
|
+
|
|
115
|
+
return list(set(control_ids)) # Remove duplicates
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def get_control_mappings_for_framework(framework: str) -> Dict[str, List[str]]:
|
|
119
|
+
"""
|
|
120
|
+
Get control mappings for a specific framework.
|
|
121
|
+
|
|
122
|
+
:param str framework: Framework name (e.g., "NIST800-53R5")
|
|
123
|
+
:return: Dictionary mapping rule names to control IDs
|
|
124
|
+
:rtype: Dict[str, List[str]]
|
|
125
|
+
"""
|
|
126
|
+
framework_upper = framework.upper().replace("-", "").replace("_", "")
|
|
127
|
+
|
|
128
|
+
if "NIST80053" in framework_upper or "NIST800" in framework_upper:
|
|
129
|
+
return NIST_80053_R5_MAPPINGS
|
|
130
|
+
|
|
131
|
+
# Add more framework mappings as needed
|
|
132
|
+
# elif "PCI" in framework_upper:
|
|
133
|
+
# return PCI_DSS_MAPPINGS
|
|
134
|
+
# elif "CIS" in framework_upper:
|
|
135
|
+
# return CIS_MAPPINGS
|
|
136
|
+
|
|
137
|
+
logger.warning(f"No built-in control mappings available for framework: {framework}")
|
|
138
|
+
return {}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def map_rule_to_controls(
|
|
142
|
+
rule_name: str,
|
|
143
|
+
rule_description: Optional[str] = None,
|
|
144
|
+
rule_tags: Optional[Dict[str, str]] = None,
|
|
145
|
+
framework: str = "NIST800-53R5",
|
|
146
|
+
) -> List[str]:
|
|
147
|
+
"""
|
|
148
|
+
Map an AWS Config rule to control IDs using multiple strategies.
|
|
149
|
+
|
|
150
|
+
Priority order:
|
|
151
|
+
1. Framework-specific mappings (conformance pack)
|
|
152
|
+
2. Rule tags (ControlID tag)
|
|
153
|
+
3. Pattern matching in rule name
|
|
154
|
+
4. Pattern matching in rule description
|
|
155
|
+
|
|
156
|
+
:param str rule_name: Config rule name
|
|
157
|
+
:param Optional[str] rule_description: Config rule description
|
|
158
|
+
:param Optional[Dict[str, str]] rule_tags: Config rule tags
|
|
159
|
+
:param str framework: Target framework
|
|
160
|
+
:return: List of mapped control IDs
|
|
161
|
+
:rtype: List[str]
|
|
162
|
+
"""
|
|
163
|
+
control_ids = []
|
|
164
|
+
|
|
165
|
+
# Strategy 1: Check framework-specific mappings
|
|
166
|
+
framework_mappings = get_control_mappings_for_framework(framework)
|
|
167
|
+
if rule_name in framework_mappings:
|
|
168
|
+
control_ids.extend(framework_mappings[rule_name])
|
|
169
|
+
logger.debug(f"Rule '{rule_name}' mapped to controls via framework mappings: {control_ids}")
|
|
170
|
+
|
|
171
|
+
# Strategy 2: Check rule tags
|
|
172
|
+
if rule_tags:
|
|
173
|
+
tag_control_ids = extract_control_ids_from_tags(rule_tags)
|
|
174
|
+
if tag_control_ids:
|
|
175
|
+
control_ids.extend(tag_control_ids)
|
|
176
|
+
logger.debug(f"Rule '{rule_name}' mapped to controls via tags: {tag_control_ids}")
|
|
177
|
+
|
|
178
|
+
# Strategy 3: Pattern matching in rule name
|
|
179
|
+
if not control_ids:
|
|
180
|
+
name_control_ids = extract_control_ids_from_rule_name(rule_name)
|
|
181
|
+
if name_control_ids:
|
|
182
|
+
control_ids.extend(name_control_ids)
|
|
183
|
+
logger.debug(f"Rule '{rule_name}' mapped to controls via name pattern: {name_control_ids}")
|
|
184
|
+
|
|
185
|
+
# Strategy 4: Pattern matching in rule description
|
|
186
|
+
if not control_ids and rule_description:
|
|
187
|
+
desc_control_ids = extract_control_ids_from_rule_name(rule_description)
|
|
188
|
+
if desc_control_ids:
|
|
189
|
+
control_ids.extend(desc_control_ids)
|
|
190
|
+
logger.debug(f"Rule '{rule_name}' mapped to controls via description pattern: {desc_control_ids}")
|
|
191
|
+
|
|
192
|
+
# Remove duplicates and sort
|
|
193
|
+
control_ids = sorted(set(control_ids))
|
|
194
|
+
|
|
195
|
+
if not control_ids:
|
|
196
|
+
logger.debug(f"Rule '{rule_name}' could not be mapped to any controls")
|
|
197
|
+
|
|
198
|
+
return control_ids
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Control Compliance Analyzer for AWS Audit Manager
|
|
5
|
+
|
|
6
|
+
This module provides enhanced control pass/fail determination based on evidence insights
|
|
7
|
+
from AWS Audit Manager. It implements the logic for determining control compliance status
|
|
8
|
+
based on compliant, non-compliant, and inconclusive evidence counts.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger("regscale")
|
|
17
|
+
|
|
18
|
+
# Constants
|
|
19
|
+
AWS_AUDIT_MANAGER_INSIGHTS_SOURCE = "AWS Audit Manager Insights"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ComplianceStatus(Enum):
|
|
23
|
+
"""Enumeration of possible compliance statuses."""
|
|
24
|
+
|
|
25
|
+
PASS = "PASS"
|
|
26
|
+
FAIL = "FAIL"
|
|
27
|
+
INCONCLUSIVE = "INCONCLUSIVE"
|
|
28
|
+
NO_DATA = "NO_DATA"
|
|
29
|
+
NOT_APPLICABLE = "NOT_APPLICABLE"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class EvidenceType(Enum):
|
|
33
|
+
"""Types of evidence compliance checks."""
|
|
34
|
+
|
|
35
|
+
COMPLIANT = "COMPLIANT"
|
|
36
|
+
NON_COMPLIANT = "NON_COMPLIANT"
|
|
37
|
+
INCONCLUSIVE = "INCONCLUSIVE"
|
|
38
|
+
NOT_APPLICABLE = "NOT_APPLICABLE"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class EvidenceInsight:
|
|
43
|
+
"""Represents a single piece of evidence with its compliance status."""
|
|
44
|
+
|
|
45
|
+
evidence_id: str
|
|
46
|
+
source: str
|
|
47
|
+
compliance_check: str
|
|
48
|
+
resource_arn: Optional[str] = None
|
|
49
|
+
timestamp: Optional[str] = None
|
|
50
|
+
attributes: Dict[str, Any] = field(default_factory=dict)
|
|
51
|
+
confidence: float = 1.0
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class ComplianceAnalysis:
|
|
56
|
+
"""Results of control compliance analysis."""
|
|
57
|
+
|
|
58
|
+
control_id: str
|
|
59
|
+
compliance_status: ComplianceStatus
|
|
60
|
+
compliant_evidence_count: int
|
|
61
|
+
noncompliant_evidence_count: int
|
|
62
|
+
inconclusive_evidence_count: int
|
|
63
|
+
not_applicable_count: int
|
|
64
|
+
total_evidence_count: int
|
|
65
|
+
compliance_score: float
|
|
66
|
+
confidence_level: float
|
|
67
|
+
reasoning: str
|
|
68
|
+
evidence_sources: List[str]
|
|
69
|
+
details: Dict[str, Any] = field(default_factory=dict)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ControlComplianceAnalyzer:
|
|
73
|
+
"""
|
|
74
|
+
Analyzes control compliance based on evidence insights from AWS Audit Manager.
|
|
75
|
+
|
|
76
|
+
This class implements the control pass/fail determination logic based on the
|
|
77
|
+
AWS Audit Manager documentation:
|
|
78
|
+
- FAIL: noncompliantEvidenceCount > 0 (any non-compliance fails the control)
|
|
79
|
+
- PASS: compliantEvidenceCount > 0 AND noncompliantEvidenceCount = 0
|
|
80
|
+
(inconclusive evidence is allowed and doesn't prevent passing)
|
|
81
|
+
- INCONCLUSIVE: Only inconclusiveEvidenceCount > 0 (no compliant/non-compliant evidence)
|
|
82
|
+
- NOT_APPLICABLE: Only notApplicableCount > 0
|
|
83
|
+
- NO DATA: All counts = 0
|
|
84
|
+
|
|
85
|
+
Note: Inconclusive evidence does not prevent a control from passing as long as there
|
|
86
|
+
is at least one piece of compliant evidence and no non-compliant evidence.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# Evidence source confidence mapping
|
|
90
|
+
EVIDENCE_CONFIDENCE_MAP = {
|
|
91
|
+
"AWS Security Hub": 0.95,
|
|
92
|
+
"AWS Config": 0.95,
|
|
93
|
+
"AWS CloudTrail": 0.75,
|
|
94
|
+
"AWS Audit Manager": 0.85,
|
|
95
|
+
"Manual": 0.60,
|
|
96
|
+
"Unknown": 0.50,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
def __init__(self, control_id: str):
|
|
100
|
+
"""
|
|
101
|
+
Initialize the analyzer for a specific control.
|
|
102
|
+
|
|
103
|
+
:param str control_id: The control identifier (e.g., "AC-2", "CC1.1")
|
|
104
|
+
"""
|
|
105
|
+
self.control_id = control_id
|
|
106
|
+
self.evidence_insights: List[EvidenceInsight] = []
|
|
107
|
+
self._compliant_count = 0
|
|
108
|
+
self._noncompliant_count = 0
|
|
109
|
+
self._inconclusive_count = 0
|
|
110
|
+
self._not_applicable_count = 0
|
|
111
|
+
|
|
112
|
+
def add_evidence_insight(self, evidence_data: Dict[str, Any]) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Add evidence from AWS Audit Manager to the analysis.
|
|
115
|
+
|
|
116
|
+
:param Dict[str, Any] evidence_data: Evidence data from AWS Audit Manager
|
|
117
|
+
"""
|
|
118
|
+
# Normalize the compliance check value
|
|
119
|
+
compliance_check = self._normalize_compliance_check(evidence_data.get("complianceCheck", ""))
|
|
120
|
+
|
|
121
|
+
# Create evidence insight
|
|
122
|
+
insight = EvidenceInsight(
|
|
123
|
+
evidence_id=evidence_data.get("id", ""),
|
|
124
|
+
source=evidence_data.get("dataSource", "AWS Audit Manager"),
|
|
125
|
+
compliance_check=compliance_check,
|
|
126
|
+
resource_arn=evidence_data.get("resourceArn"),
|
|
127
|
+
timestamp=evidence_data.get("time"),
|
|
128
|
+
attributes=evidence_data.get("attributes", {}),
|
|
129
|
+
confidence=self._get_evidence_confidence(evidence_data.get("dataSource", "Unknown")),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
self.evidence_insights.append(insight)
|
|
133
|
+
|
|
134
|
+
# Update counts
|
|
135
|
+
self._update_evidence_counts(compliance_check)
|
|
136
|
+
|
|
137
|
+
def add_evidence_from_insights_api(self, insights_data: Dict[str, Any]) -> None:
|
|
138
|
+
"""
|
|
139
|
+
Add evidence from AWS Audit Manager Control Insights API.
|
|
140
|
+
|
|
141
|
+
:param Dict[str, Any] insights_data: Control insights data from API
|
|
142
|
+
"""
|
|
143
|
+
# Extract evidence counts from insights API response
|
|
144
|
+
evidence_insights = insights_data.get("evidenceInsights", {})
|
|
145
|
+
|
|
146
|
+
compliant_count = evidence_insights.get("compliantEvidenceCount", 0)
|
|
147
|
+
noncompliant_count = evidence_insights.get("noncompliantEvidenceCount", 0)
|
|
148
|
+
inconclusive_count = evidence_insights.get("inconclusiveEvidenceCount", 0)
|
|
149
|
+
|
|
150
|
+
# Create synthetic evidence insights for counted evidence
|
|
151
|
+
for _ in range(compliant_count):
|
|
152
|
+
self._compliant_count += 1
|
|
153
|
+
self.evidence_insights.append(
|
|
154
|
+
EvidenceInsight(
|
|
155
|
+
evidence_id=f"insights-compliant-{self._compliant_count}",
|
|
156
|
+
source=AWS_AUDIT_MANAGER_INSIGHTS_SOURCE,
|
|
157
|
+
compliance_check="COMPLIANT",
|
|
158
|
+
confidence=0.9,
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
for _ in range(noncompliant_count):
|
|
163
|
+
self._noncompliant_count += 1
|
|
164
|
+
self.evidence_insights.append(
|
|
165
|
+
EvidenceInsight(
|
|
166
|
+
evidence_id=f"insights-noncompliant-{self._noncompliant_count}",
|
|
167
|
+
source=AWS_AUDIT_MANAGER_INSIGHTS_SOURCE,
|
|
168
|
+
compliance_check="NON_COMPLIANT",
|
|
169
|
+
confidence=0.9,
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
for _ in range(inconclusive_count):
|
|
174
|
+
self._inconclusive_count += 1
|
|
175
|
+
self.evidence_insights.append(
|
|
176
|
+
EvidenceInsight(
|
|
177
|
+
evidence_id=f"insights-inconclusive-{self._inconclusive_count}",
|
|
178
|
+
source=AWS_AUDIT_MANAGER_INSIGHTS_SOURCE,
|
|
179
|
+
compliance_check="INCONCLUSIVE",
|
|
180
|
+
confidence=0.7,
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def determine_control_status(self) -> Tuple[str, Dict[str, Any]]:
|
|
185
|
+
"""
|
|
186
|
+
Determine control pass/fail based on evidence counts.
|
|
187
|
+
|
|
188
|
+
This implements the AWS Audit Manager logic:
|
|
189
|
+
- FAIL: noncompliantEvidenceCount > 0 (any non-compliance = fail)
|
|
190
|
+
- PASS: compliantEvidenceCount > 0 AND noncompliantEvidenceCount = 0
|
|
191
|
+
(inconclusive evidence is allowed and doesn't prevent passing)
|
|
192
|
+
- INCONCLUSIVE: Only inconclusiveEvidenceCount > 0 (no compliant or non-compliant)
|
|
193
|
+
- NOT_APPLICABLE: Only notApplicableCount > 0
|
|
194
|
+
- NO DATA: All counts = 0
|
|
195
|
+
|
|
196
|
+
:return: Tuple of (status, details)
|
|
197
|
+
:rtype: Tuple[str, Dict[str, Any]]
|
|
198
|
+
"""
|
|
199
|
+
# Check for failures first (any non-compliant evidence = FAIL)
|
|
200
|
+
if self._noncompliant_count > 0:
|
|
201
|
+
return ComplianceStatus.FAIL.value, {
|
|
202
|
+
"reason": "Evidence indicates non-compliance",
|
|
203
|
+
"noncompliant_count": self._noncompliant_count,
|
|
204
|
+
"compliant_count": self._compliant_count,
|
|
205
|
+
"inconclusive_count": self._inconclusive_count,
|
|
206
|
+
"not_applicable_count": self._not_applicable_count,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# Check for pass (compliant evidence with no non-compliant, inconclusive evidence allowed)
|
|
210
|
+
if self._compliant_count > 0 and self._noncompliant_count == 0:
|
|
211
|
+
# Determine the appropriate reason message
|
|
212
|
+
if self._inconclusive_count > 0:
|
|
213
|
+
reason = "Compliant evidence found with no non-compliance (some evidence inconclusive)"
|
|
214
|
+
else:
|
|
215
|
+
reason = "All evidence indicates compliance"
|
|
216
|
+
|
|
217
|
+
return ComplianceStatus.PASS.value, {
|
|
218
|
+
"reason": reason,
|
|
219
|
+
"compliant_count": self._compliant_count,
|
|
220
|
+
"inconclusive_count": self._inconclusive_count,
|
|
221
|
+
"not_applicable_count": self._not_applicable_count,
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# Check for inconclusive (only inconclusive evidence)
|
|
225
|
+
if self._inconclusive_count > 0 and self._compliant_count == 0 and self._noncompliant_count == 0:
|
|
226
|
+
return ComplianceStatus.INCONCLUSIVE.value, {
|
|
227
|
+
"reason": "Only inconclusive evidence available",
|
|
228
|
+
"inconclusive_count": self._inconclusive_count,
|
|
229
|
+
"not_applicable_count": self._not_applicable_count,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# Check for not applicable (only not applicable evidence)
|
|
233
|
+
if (
|
|
234
|
+
self._not_applicable_count > 0
|
|
235
|
+
and self._compliant_count == 0
|
|
236
|
+
and self._noncompliant_count == 0
|
|
237
|
+
and self._inconclusive_count == 0
|
|
238
|
+
):
|
|
239
|
+
return ComplianceStatus.NOT_APPLICABLE.value, {
|
|
240
|
+
"reason": "Evidence is not applicable to this control",
|
|
241
|
+
"not_applicable_count": self._not_applicable_count,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# No data available
|
|
245
|
+
return ComplianceStatus.NO_DATA.value, {
|
|
246
|
+
"reason": "No evidence available for assessment",
|
|
247
|
+
"total_evidence_checked": len(self.evidence_insights),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
def get_compliance_analysis(self) -> ComplianceAnalysis:
|
|
251
|
+
"""
|
|
252
|
+
Get comprehensive compliance analysis for the control.
|
|
253
|
+
|
|
254
|
+
:return: Detailed compliance analysis
|
|
255
|
+
:rtype: ComplianceAnalysis
|
|
256
|
+
"""
|
|
257
|
+
status_str, details = self.determine_control_status()
|
|
258
|
+
status = ComplianceStatus(status_str)
|
|
259
|
+
|
|
260
|
+
# Calculate compliance score
|
|
261
|
+
compliance_score = self._calculate_compliance_score()
|
|
262
|
+
|
|
263
|
+
# Calculate confidence level
|
|
264
|
+
confidence_level = self._calculate_confidence_level()
|
|
265
|
+
|
|
266
|
+
# Get unique evidence sources
|
|
267
|
+
evidence_sources = list({insight.source for insight in self.evidence_insights})
|
|
268
|
+
|
|
269
|
+
return ComplianceAnalysis(
|
|
270
|
+
control_id=self.control_id,
|
|
271
|
+
compliance_status=status,
|
|
272
|
+
compliant_evidence_count=self._compliant_count,
|
|
273
|
+
noncompliant_evidence_count=self._noncompliant_count,
|
|
274
|
+
inconclusive_evidence_count=self._inconclusive_count,
|
|
275
|
+
not_applicable_count=self._not_applicable_count,
|
|
276
|
+
total_evidence_count=len(self.evidence_insights),
|
|
277
|
+
compliance_score=compliance_score,
|
|
278
|
+
confidence_level=confidence_level,
|
|
279
|
+
reasoning=details.get("reason", ""),
|
|
280
|
+
evidence_sources=evidence_sources,
|
|
281
|
+
details=details,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
def get_compliance_score(self) -> float:
|
|
285
|
+
"""
|
|
286
|
+
Calculate compliance score (0-1) based on evidence.
|
|
287
|
+
|
|
288
|
+
:return: Compliance score between 0 and 1
|
|
289
|
+
:rtype: float
|
|
290
|
+
"""
|
|
291
|
+
return self._calculate_compliance_score()
|
|
292
|
+
|
|
293
|
+
def get_confidence_level(self) -> float:
|
|
294
|
+
"""
|
|
295
|
+
Calculate confidence level based on evidence sources and quantity.
|
|
296
|
+
|
|
297
|
+
:return: Confidence level between 0 and 1
|
|
298
|
+
:rtype: float
|
|
299
|
+
"""
|
|
300
|
+
return self._calculate_confidence_level()
|
|
301
|
+
|
|
302
|
+
def get_compliance_setting_note(self) -> str:
|
|
303
|
+
"""
|
|
304
|
+
Get a note about how the status will be mapped based on compliance settings.
|
|
305
|
+
|
|
306
|
+
This is informational - the actual mapping is done by ComplianceIntegration
|
|
307
|
+
based on the security plan's compliance settings.
|
|
308
|
+
|
|
309
|
+
:return: Informational note about status mapping
|
|
310
|
+
:rtype: str
|
|
311
|
+
"""
|
|
312
|
+
status_str, details = self.determine_control_status()
|
|
313
|
+
|
|
314
|
+
if status_str == "PASS":
|
|
315
|
+
note = (
|
|
316
|
+
"PASS status will be mapped to compliance-specific value: "
|
|
317
|
+
"DoD/RMF → 'Implemented', FedRAMP → 'Fully Implemented', "
|
|
318
|
+
"Default → 'Fully Implemented'"
|
|
319
|
+
)
|
|
320
|
+
# Add clarification if there's inconclusive evidence
|
|
321
|
+
if details.get("inconclusive_count", 0) > 0:
|
|
322
|
+
note += " (includes inconclusive evidence that doesn't affect passing)"
|
|
323
|
+
return note
|
|
324
|
+
elif status_str == "FAIL":
|
|
325
|
+
return (
|
|
326
|
+
"FAIL status will be mapped to compliance-specific value: "
|
|
327
|
+
"DoD/RMF → 'Not Implemented' or 'Planned', FedRAMP → 'In Remediation' or 'Partially Implemented', "
|
|
328
|
+
"Default → 'In Remediation'"
|
|
329
|
+
)
|
|
330
|
+
elif status_str == "NOT_APPLICABLE":
|
|
331
|
+
return "NOT_APPLICABLE status will be mapped to 'Not Applicable' across all compliance settings"
|
|
332
|
+
else:
|
|
333
|
+
return "Status cannot be determined - control implementation will not be updated"
|
|
334
|
+
|
|
335
|
+
def _normalize_compliance_check(self, compliance_check: str) -> str:
|
|
336
|
+
"""
|
|
337
|
+
Normalize compliance check values from various AWS services.
|
|
338
|
+
|
|
339
|
+
:param str compliance_check: Raw compliance check value
|
|
340
|
+
:return: Normalized compliance status
|
|
341
|
+
:rtype: str
|
|
342
|
+
"""
|
|
343
|
+
if not compliance_check:
|
|
344
|
+
return "INCONCLUSIVE"
|
|
345
|
+
|
|
346
|
+
check_upper = compliance_check.upper()
|
|
347
|
+
|
|
348
|
+
# Map success values
|
|
349
|
+
if check_upper in ["COMPLIANT", "PASS", "PASSED", "SUCCESS"]:
|
|
350
|
+
return "COMPLIANT"
|
|
351
|
+
|
|
352
|
+
# Map failure values
|
|
353
|
+
if check_upper in ["NON_COMPLIANT", "NON-COMPLIANT", "FAIL", "FAILED", "FAILURE"]:
|
|
354
|
+
return "NON_COMPLIANT"
|
|
355
|
+
|
|
356
|
+
# Map not applicable
|
|
357
|
+
if check_upper in ["NOT_APPLICABLE", "NOT-APPLICABLE", "N/A", "NA"]:
|
|
358
|
+
return "NOT_APPLICABLE"
|
|
359
|
+
|
|
360
|
+
# Default to inconclusive
|
|
361
|
+
return "INCONCLUSIVE"
|
|
362
|
+
|
|
363
|
+
def _update_evidence_counts(self, compliance_check: str) -> None:
|
|
364
|
+
"""
|
|
365
|
+
Update evidence counts based on compliance check value.
|
|
366
|
+
|
|
367
|
+
:param str compliance_check: Normalized compliance check value
|
|
368
|
+
"""
|
|
369
|
+
if compliance_check == "COMPLIANT":
|
|
370
|
+
self._compliant_count += 1
|
|
371
|
+
elif compliance_check == "NON_COMPLIANT":
|
|
372
|
+
self._noncompliant_count += 1
|
|
373
|
+
elif compliance_check == "NOT_APPLICABLE":
|
|
374
|
+
self._not_applicable_count += 1
|
|
375
|
+
else:
|
|
376
|
+
self._inconclusive_count += 1
|
|
377
|
+
|
|
378
|
+
def _get_evidence_confidence(self, source: str) -> float:
|
|
379
|
+
"""
|
|
380
|
+
Get confidence level for an evidence source.
|
|
381
|
+
|
|
382
|
+
:param str source: Evidence source name
|
|
383
|
+
:return: Confidence level between 0 and 1
|
|
384
|
+
:rtype: float
|
|
385
|
+
"""
|
|
386
|
+
return self.EVIDENCE_CONFIDENCE_MAP.get(source, 0.5)
|
|
387
|
+
|
|
388
|
+
def _calculate_compliance_score(self) -> float:
|
|
389
|
+
"""
|
|
390
|
+
Calculate compliance score based on evidence.
|
|
391
|
+
|
|
392
|
+
Score calculation:
|
|
393
|
+
- 1.0: All evidence is compliant
|
|
394
|
+
- 0.0: Any evidence is non-compliant
|
|
395
|
+
- 0.5: Only inconclusive evidence
|
|
396
|
+
- Weighted average based on evidence counts
|
|
397
|
+
|
|
398
|
+
:return: Compliance score between 0 and 1
|
|
399
|
+
:rtype: float
|
|
400
|
+
"""
|
|
401
|
+
total_evidence = self._compliant_count + self._noncompliant_count + self._inconclusive_count
|
|
402
|
+
|
|
403
|
+
if total_evidence == 0:
|
|
404
|
+
return 0.0
|
|
405
|
+
|
|
406
|
+
# If any non-compliant evidence, score is 0
|
|
407
|
+
if self._noncompliant_count > 0:
|
|
408
|
+
return 0.0
|
|
409
|
+
|
|
410
|
+
# If all compliant, score is 1
|
|
411
|
+
if self._compliant_count > 0 and self._inconclusive_count == 0:
|
|
412
|
+
return 1.0
|
|
413
|
+
|
|
414
|
+
# Mixed compliant and inconclusive
|
|
415
|
+
if self._compliant_count > 0:
|
|
416
|
+
return self._compliant_count / total_evidence
|
|
417
|
+
|
|
418
|
+
# Only inconclusive
|
|
419
|
+
return 0.5
|
|
420
|
+
|
|
421
|
+
def _calculate_confidence_level(self) -> float:
|
|
422
|
+
"""
|
|
423
|
+
Calculate confidence level based on evidence sources and quantity.
|
|
424
|
+
|
|
425
|
+
:return: Confidence level between 0 and 1
|
|
426
|
+
:rtype: float
|
|
427
|
+
"""
|
|
428
|
+
if not self.evidence_insights:
|
|
429
|
+
return 0.0
|
|
430
|
+
|
|
431
|
+
# Calculate weighted average confidence
|
|
432
|
+
total_confidence = sum(insight.confidence for insight in self.evidence_insights)
|
|
433
|
+
avg_confidence = total_confidence / len(self.evidence_insights)
|
|
434
|
+
|
|
435
|
+
# Adjust for evidence quantity (more evidence = higher confidence)
|
|
436
|
+
quantity_factor = min(1.0, len(self.evidence_insights) / 10.0)
|
|
437
|
+
|
|
438
|
+
# Combine average confidence with quantity factor
|
|
439
|
+
return (avg_confidence * 0.7) + (quantity_factor * 0.3)
|