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,283 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""AWS Evidence Generator for SSP compliance documentation"""
|
|
4
|
+
|
|
5
|
+
import gzip
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
from io import BytesIO
|
|
10
|
+
from typing import List, Optional
|
|
11
|
+
|
|
12
|
+
from regscale.core.app.api import Api
|
|
13
|
+
from regscale.models.regscale_models.evidence import Evidence
|
|
14
|
+
from regscale.models.regscale_models.evidence_mapping import EvidenceMapping
|
|
15
|
+
from regscale.models.regscale_models.file import File
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("regscale")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AWSEvidenceGenerator:
|
|
21
|
+
"""Generate compliance evidence from AWS security findings"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, api: Api, ssp_id: Optional[int] = None):
|
|
24
|
+
"""
|
|
25
|
+
Initialize evidence generator
|
|
26
|
+
|
|
27
|
+
:param Api api: RegScale API instance
|
|
28
|
+
:param Optional[int] ssp_id: Security Plan ID to link evidence
|
|
29
|
+
"""
|
|
30
|
+
self.api = api
|
|
31
|
+
self.ssp_id = ssp_id
|
|
32
|
+
|
|
33
|
+
def create_evidence_from_scan(
|
|
34
|
+
self,
|
|
35
|
+
service_name: str,
|
|
36
|
+
findings: List[dict],
|
|
37
|
+
ocsf_data: Optional[List[dict]] = None,
|
|
38
|
+
update_frequency: int = 30,
|
|
39
|
+
control_ids: Optional[List[int]] = None,
|
|
40
|
+
) -> Optional[Evidence]:
|
|
41
|
+
"""
|
|
42
|
+
Create evidence record from AWS service scan
|
|
43
|
+
|
|
44
|
+
:param str service_name: AWS service name (GuardDuty, SecurityHub, etc.)
|
|
45
|
+
:param List[dict] findings: List of AWS findings (native format)
|
|
46
|
+
:param Optional[List[dict]] ocsf_data: OCSF-formatted findings
|
|
47
|
+
:param int update_frequency: Evidence update frequency in days (default: 30)
|
|
48
|
+
:param Optional[List[int]] control_ids: Control IDs to link evidence
|
|
49
|
+
:return: Created Evidence object or None
|
|
50
|
+
:rtype: Optional[Evidence]
|
|
51
|
+
"""
|
|
52
|
+
if not findings:
|
|
53
|
+
logger.warning("No findings provided, skipping evidence creation")
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
# Generate evidence title with timestamp
|
|
57
|
+
scan_date = datetime.now().strftime("%Y-%m-%d")
|
|
58
|
+
title = f"{service_name} Findings Scan - {scan_date}"
|
|
59
|
+
|
|
60
|
+
# Create description with finding summary
|
|
61
|
+
total_findings = len(findings)
|
|
62
|
+
severity_counts = self._count_severities(findings, service_name)
|
|
63
|
+
description = self._build_evidence_description(service_name, total_findings, severity_counts, ocsf_data)
|
|
64
|
+
|
|
65
|
+
# Calculate due date based on update frequency
|
|
66
|
+
due_date = (datetime.now() + timedelta(days=update_frequency)).isoformat()
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# Create evidence record
|
|
70
|
+
evidence = Evidence(
|
|
71
|
+
title=title,
|
|
72
|
+
description=description,
|
|
73
|
+
status="Collected",
|
|
74
|
+
updateFrequency=update_frequency,
|
|
75
|
+
dueDate=due_date,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Create evidence in RegScale
|
|
79
|
+
created_evidence = evidence.create()
|
|
80
|
+
if not created_evidence or not created_evidence.id:
|
|
81
|
+
logger.error("Failed to create evidence record")
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
logger.info("Created evidence record %s: %s", created_evidence.id, title)
|
|
85
|
+
|
|
86
|
+
# Upload findings as file attachments
|
|
87
|
+
self._attach_findings_files(
|
|
88
|
+
created_evidence.id,
|
|
89
|
+
findings,
|
|
90
|
+
ocsf_data,
|
|
91
|
+
service_name,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Link evidence to SSP if provided
|
|
95
|
+
if self.ssp_id:
|
|
96
|
+
self._link_to_ssp(created_evidence.id)
|
|
97
|
+
|
|
98
|
+
# Link evidence to specific controls if provided
|
|
99
|
+
if control_ids:
|
|
100
|
+
self._link_to_controls(created_evidence.id, control_ids)
|
|
101
|
+
|
|
102
|
+
return created_evidence
|
|
103
|
+
|
|
104
|
+
except Exception as ex:
|
|
105
|
+
logger.error("Failed to create evidence: %s", ex)
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
def _count_severities(self, findings: List[dict], service_name: str) -> dict:
|
|
109
|
+
"""
|
|
110
|
+
Count findings by severity level
|
|
111
|
+
|
|
112
|
+
:param List[dict] findings: AWS findings
|
|
113
|
+
:param str service_name: AWS service name for severity field mapping
|
|
114
|
+
:return: Dictionary with severity counts
|
|
115
|
+
:rtype: dict
|
|
116
|
+
"""
|
|
117
|
+
severity_counts = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0, "INFO": 0}
|
|
118
|
+
|
|
119
|
+
for finding in findings:
|
|
120
|
+
# Map service-specific severity fields
|
|
121
|
+
if service_name == "GuardDuty":
|
|
122
|
+
severity = finding.get("Severity", 0)
|
|
123
|
+
# GuardDuty uses numeric severity (1.0-8.9)
|
|
124
|
+
if severity >= 7.0:
|
|
125
|
+
severity_counts["HIGH"] += 1
|
|
126
|
+
elif severity >= 4.0:
|
|
127
|
+
severity_counts["MEDIUM"] += 1
|
|
128
|
+
else:
|
|
129
|
+
severity_counts["LOW"] += 1
|
|
130
|
+
|
|
131
|
+
elif service_name == "SecurityHub":
|
|
132
|
+
# Security Hub uses normalized severity
|
|
133
|
+
severity_label = finding.get("Severity", {}).get("Label", "INFO")
|
|
134
|
+
severity_counts[severity_label] = severity_counts.get(severity_label, 0) + 1
|
|
135
|
+
|
|
136
|
+
elif service_name == "CloudTrail":
|
|
137
|
+
# CloudTrail events don't have severity, count as INFO
|
|
138
|
+
severity_counts["INFO"] += 1
|
|
139
|
+
|
|
140
|
+
return severity_counts
|
|
141
|
+
|
|
142
|
+
def _build_evidence_description(
|
|
143
|
+
self,
|
|
144
|
+
service_name: str,
|
|
145
|
+
total_findings: int,
|
|
146
|
+
severity_counts: dict,
|
|
147
|
+
ocsf_data: Optional[List[dict]],
|
|
148
|
+
) -> str:
|
|
149
|
+
"""
|
|
150
|
+
Build evidence description with finding summary
|
|
151
|
+
|
|
152
|
+
:param str service_name: AWS service name
|
|
153
|
+
:param int total_findings: Total number of findings
|
|
154
|
+
:param dict severity_counts: Severity breakdown
|
|
155
|
+
:param Optional[List[dict]] ocsf_data: OCSF-formatted findings
|
|
156
|
+
:return: Evidence description text
|
|
157
|
+
:rtype: str
|
|
158
|
+
"""
|
|
159
|
+
description_parts = [
|
|
160
|
+
f"Automated evidence collection from AWS {service_name}.",
|
|
161
|
+
f"Total findings: {total_findings}",
|
|
162
|
+
"",
|
|
163
|
+
"Severity Breakdown:",
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
for severity, count in severity_counts.items():
|
|
167
|
+
if count > 0:
|
|
168
|
+
description_parts.append(f" - {severity}: {count}")
|
|
169
|
+
|
|
170
|
+
description_parts.extend(["", "Files attached:"])
|
|
171
|
+
description_parts.append(f" - {service_name.lower()}_findings_native.jsonl.gz (AWS native format, compressed)")
|
|
172
|
+
|
|
173
|
+
if ocsf_data:
|
|
174
|
+
description_parts.append(
|
|
175
|
+
f" - {service_name.lower()}_findings_ocsf.jsonl.gz (OCSF normalized format, compressed)"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return "\n".join(description_parts)
|
|
179
|
+
|
|
180
|
+
def _attach_findings_files(
|
|
181
|
+
self,
|
|
182
|
+
evidence_id: int,
|
|
183
|
+
findings: List[dict],
|
|
184
|
+
ocsf_data: Optional[List[dict]],
|
|
185
|
+
service_name: str,
|
|
186
|
+
) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Upload findings as file attachments to evidence
|
|
189
|
+
|
|
190
|
+
:param int evidence_id: Evidence record ID
|
|
191
|
+
:param List[dict] findings: Native AWS findings
|
|
192
|
+
:param Optional[List[dict]] ocsf_data: OCSF-formatted findings
|
|
193
|
+
:param str service_name: AWS service name
|
|
194
|
+
"""
|
|
195
|
+
# Upload native findings as compressed JSONL
|
|
196
|
+
native_jsonl = "\n".join([json.dumps(f) for f in findings])
|
|
197
|
+
|
|
198
|
+
# Compress the JSONL data
|
|
199
|
+
compressed_buffer = BytesIO()
|
|
200
|
+
with gzip.open(compressed_buffer, "wt", encoding="utf-8", compresslevel=9) as gz_file:
|
|
201
|
+
gz_file.write(native_jsonl)
|
|
202
|
+
|
|
203
|
+
compressed_data = compressed_buffer.getvalue()
|
|
204
|
+
|
|
205
|
+
success = File.upload_file_to_regscale(
|
|
206
|
+
file_name=f"{service_name.lower()}_findings_native.jsonl.gz",
|
|
207
|
+
parent_id=evidence_id,
|
|
208
|
+
parent_module="evidence",
|
|
209
|
+
api=self.api,
|
|
210
|
+
file_data=compressed_data,
|
|
211
|
+
tags=f"aws,{service_name.lower()},native,compressed",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if success:
|
|
215
|
+
logger.info("Uploaded compressed native findings file for evidence %s", evidence_id)
|
|
216
|
+
else:
|
|
217
|
+
logger.warning("Failed to upload compressed native findings file for evidence %s", evidence_id)
|
|
218
|
+
|
|
219
|
+
# Upload OCSF findings if available
|
|
220
|
+
if ocsf_data:
|
|
221
|
+
ocsf_jsonl = "\n".join([json.dumps(f) for f in ocsf_data])
|
|
222
|
+
|
|
223
|
+
# Compress the OCSF JSONL data
|
|
224
|
+
compressed_buffer = BytesIO()
|
|
225
|
+
with gzip.open(compressed_buffer, "wt", encoding="utf-8", compresslevel=9) as gz_file:
|
|
226
|
+
gz_file.write(ocsf_jsonl)
|
|
227
|
+
|
|
228
|
+
compressed_data = compressed_buffer.getvalue()
|
|
229
|
+
|
|
230
|
+
success = File.upload_file_to_regscale(
|
|
231
|
+
file_name=f"{service_name.lower()}_findings_ocsf.jsonl.gz",
|
|
232
|
+
parent_id=evidence_id,
|
|
233
|
+
parent_module="evidence",
|
|
234
|
+
api=self.api,
|
|
235
|
+
file_data=compressed_data,
|
|
236
|
+
tags=f"aws,{service_name.lower()},ocsf,compressed",
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if success:
|
|
240
|
+
logger.info("Uploaded compressed OCSF findings file for evidence %s", evidence_id)
|
|
241
|
+
else:
|
|
242
|
+
logger.warning("Failed to upload compressed OCSF findings file for evidence %s", evidence_id)
|
|
243
|
+
|
|
244
|
+
def _link_to_ssp(self, evidence_id: int) -> None:
|
|
245
|
+
"""
|
|
246
|
+
Link evidence to Security Plan
|
|
247
|
+
|
|
248
|
+
:param int evidence_id: Evidence record ID
|
|
249
|
+
"""
|
|
250
|
+
if not self.ssp_id:
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
mapping = EvidenceMapping(
|
|
254
|
+
evidenceID=evidence_id,
|
|
255
|
+
mappedID=self.ssp_id,
|
|
256
|
+
mappingType="securityplans",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
mapping.create()
|
|
261
|
+
logger.info("Linked evidence %s to SSP %s", evidence_id, self.ssp_id)
|
|
262
|
+
except Exception as ex:
|
|
263
|
+
logger.warning("Failed to link evidence to SSP: %s", ex)
|
|
264
|
+
|
|
265
|
+
def _link_to_controls(self, evidence_id: int, control_ids: List[int]) -> None:
|
|
266
|
+
"""
|
|
267
|
+
Link evidence to specific security controls
|
|
268
|
+
|
|
269
|
+
:param int evidence_id: Evidence record ID
|
|
270
|
+
:param List[int] control_ids: List of control IDs
|
|
271
|
+
"""
|
|
272
|
+
for control_id in control_ids:
|
|
273
|
+
mapping = EvidenceMapping(
|
|
274
|
+
evidenceID=evidence_id,
|
|
275
|
+
mappedID=control_id,
|
|
276
|
+
mappingType="securityControlAssessments",
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
mapping.create()
|
|
281
|
+
logger.info("Linked evidence %s to control %s", evidence_id, control_id)
|
|
282
|
+
except Exception as ex:
|
|
283
|
+
logger.warning("Failed to link evidence to control %s: %s", control_id, ex)
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""AWS GuardDuty Control Mappings for RegScale Compliance Integration."""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger("regscale")
|
|
9
|
+
|
|
10
|
+
# NIST 800-53 R5 Control Mappings for AWS GuardDuty
|
|
11
|
+
GUARDDUTY_CONTROL_MAPPINGS = {
|
|
12
|
+
"SI-4": {
|
|
13
|
+
"name": "System Monitoring",
|
|
14
|
+
"description": "Monitor the system to detect attacks and indicators of potential attacks",
|
|
15
|
+
"checks": {
|
|
16
|
+
"detector_enabled": {
|
|
17
|
+
"weight": 100,
|
|
18
|
+
"pass_criteria": "GuardDuty detector is enabled and actively monitoring",
|
|
19
|
+
"fail_criteria": "GuardDuty detector is disabled or suspended",
|
|
20
|
+
},
|
|
21
|
+
"findings_processed": {
|
|
22
|
+
"weight": 90,
|
|
23
|
+
"pass_criteria": "Active findings are reviewed and remediated",
|
|
24
|
+
"fail_criteria": "High severity findings remain unaddressed",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
"IR-4": {
|
|
29
|
+
"name": "Incident Handling",
|
|
30
|
+
"description": "Implement incident handling capability for security incidents",
|
|
31
|
+
"checks": {
|
|
32
|
+
"high_severity_findings": {
|
|
33
|
+
"weight": 100,
|
|
34
|
+
"pass_criteria": "No unaddressed high severity findings",
|
|
35
|
+
"fail_criteria": "High severity findings detected and not remediated",
|
|
36
|
+
},
|
|
37
|
+
"incident_response": {
|
|
38
|
+
"weight": 90,
|
|
39
|
+
"pass_criteria": "Findings integrated with incident response workflow",
|
|
40
|
+
"fail_criteria": "Findings not tracked in incident management system",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
"IR-5": {
|
|
45
|
+
"name": "Incident Monitoring",
|
|
46
|
+
"description": "Track and document system security incidents",
|
|
47
|
+
"checks": {
|
|
48
|
+
"finding_tracking": {
|
|
49
|
+
"weight": 100,
|
|
50
|
+
"pass_criteria": "All findings tracked and documented",
|
|
51
|
+
"fail_criteria": "Findings not systematically tracked",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
"SI-3": {
|
|
56
|
+
"name": "Malicious Code Protection",
|
|
57
|
+
"description": "Implement malicious code protection mechanisms",
|
|
58
|
+
"checks": {
|
|
59
|
+
"malware_detection": {
|
|
60
|
+
"weight": 100,
|
|
61
|
+
"pass_criteria": "GuardDuty detecting and alerting on malware activities",
|
|
62
|
+
"fail_criteria": "Malware protection findings not enabled or monitored",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
"RA-5": {
|
|
67
|
+
"name": "Vulnerability Monitoring and Scanning",
|
|
68
|
+
"description": "Monitor and scan for vulnerabilities in the system",
|
|
69
|
+
"checks": {
|
|
70
|
+
"threat_intelligence": {
|
|
71
|
+
"weight": 100,
|
|
72
|
+
"pass_criteria": "GuardDuty threat intelligence enabled and current",
|
|
73
|
+
"fail_criteria": "Threat intelligence feeds not enabled or outdated",
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Severity mapping (GuardDuty uses 0-10 scale)
|
|
80
|
+
SEVERITY_MAPPING = {
|
|
81
|
+
"LOW": (0.1, 3.9),
|
|
82
|
+
"MEDIUM": (4.0, 6.9),
|
|
83
|
+
"HIGH": (7.0, 8.9),
|
|
84
|
+
"CRITICAL": (9.0, 10.0),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Finding types that may contain CVEs
|
|
88
|
+
CVE_RELATED_FINDING_TYPES = [
|
|
89
|
+
"UnauthorizedAccess:EC2/MaliciousIPCaller",
|
|
90
|
+
"CryptoCurrency:EC2/BitcoinTool",
|
|
91
|
+
"Trojan:EC2/",
|
|
92
|
+
"Backdoor:EC2/",
|
|
93
|
+
"Behavior:EC2/NetworkPortUnusual",
|
|
94
|
+
"Recon:EC2/",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class GuardDutyControlMapper:
|
|
99
|
+
"""Map AWS GuardDuty findings to compliance control status."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, framework: str = "NIST800-53R5"):
|
|
102
|
+
"""
|
|
103
|
+
Initialize GuardDuty control mapper.
|
|
104
|
+
|
|
105
|
+
:param str framework: Compliance framework
|
|
106
|
+
"""
|
|
107
|
+
self.framework = framework
|
|
108
|
+
self.mappings = GUARDDUTY_CONTROL_MAPPINGS
|
|
109
|
+
|
|
110
|
+
def assess_guardduty_compliance(self, guardduty_data: Dict) -> Dict[str, str]:
|
|
111
|
+
"""
|
|
112
|
+
Assess GuardDuty compliance against all mapped controls.
|
|
113
|
+
|
|
114
|
+
:param Dict guardduty_data: GuardDuty detectors and findings
|
|
115
|
+
:return: Dictionary mapping control IDs to compliance results (PASS/FAIL)
|
|
116
|
+
:rtype: Dict[str, str]
|
|
117
|
+
"""
|
|
118
|
+
results = {}
|
|
119
|
+
|
|
120
|
+
if self.framework == "NIST800-53R5":
|
|
121
|
+
results["SI-4"] = self._assess_si4(guardduty_data)
|
|
122
|
+
results["IR-4"] = self._assess_ir4(guardduty_data)
|
|
123
|
+
results["IR-5"] = self._assess_ir5(guardduty_data)
|
|
124
|
+
results["SI-3"] = self._assess_si3(guardduty_data)
|
|
125
|
+
results["RA-5"] = self._assess_ra5(guardduty_data)
|
|
126
|
+
|
|
127
|
+
return results
|
|
128
|
+
|
|
129
|
+
def _assess_si4(self, guardduty_data: Dict) -> str:
|
|
130
|
+
"""
|
|
131
|
+
Assess SI-4 (System Monitoring) compliance.
|
|
132
|
+
|
|
133
|
+
:param Dict guardduty_data: GuardDuty data
|
|
134
|
+
:return: Compliance result (PASS/FAIL)
|
|
135
|
+
:rtype: str
|
|
136
|
+
"""
|
|
137
|
+
detectors = guardduty_data.get("Detectors", [])
|
|
138
|
+
|
|
139
|
+
if not detectors:
|
|
140
|
+
logger.debug("GuardDuty FAILS SI-4: No detectors configured")
|
|
141
|
+
return "FAIL"
|
|
142
|
+
|
|
143
|
+
# Check if any detector is disabled
|
|
144
|
+
disabled_detectors = [d for d in detectors if d.get("Status") != "ENABLED"]
|
|
145
|
+
if disabled_detectors:
|
|
146
|
+
logger.debug(f"GuardDuty FAILS SI-4: {len(disabled_detectors)} detector(s) disabled")
|
|
147
|
+
return "FAIL"
|
|
148
|
+
|
|
149
|
+
logger.debug(f"GuardDuty PASSES SI-4: {len(detectors)} detector(s) enabled and monitoring")
|
|
150
|
+
return "PASS"
|
|
151
|
+
|
|
152
|
+
def _assess_ir4(self, guardduty_data: Dict) -> str:
|
|
153
|
+
"""
|
|
154
|
+
Assess IR-4 (Incident Handling) compliance.
|
|
155
|
+
|
|
156
|
+
:param Dict guardduty_data: GuardDuty data
|
|
157
|
+
:return: Compliance result (PASS/FAIL)
|
|
158
|
+
:rtype: str
|
|
159
|
+
"""
|
|
160
|
+
findings = guardduty_data.get("Findings", [])
|
|
161
|
+
|
|
162
|
+
# Check for high/critical severity unaddressed findings
|
|
163
|
+
high_severity_findings = [
|
|
164
|
+
f for f in findings if self._get_severity_level(f.get("Severity", 0)) in ["HIGH", "CRITICAL"]
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
if high_severity_findings:
|
|
168
|
+
logger.debug(
|
|
169
|
+
f"GuardDuty FAILS IR-4: {len(high_severity_findings)} high/critical severity "
|
|
170
|
+
"findings requiring incident response"
|
|
171
|
+
)
|
|
172
|
+
return "FAIL"
|
|
173
|
+
|
|
174
|
+
logger.debug("GuardDuty PASSES IR-4: No high severity findings requiring immediate attention")
|
|
175
|
+
return "PASS"
|
|
176
|
+
|
|
177
|
+
def _assess_ir5(self, guardduty_data: Dict) -> str:
|
|
178
|
+
"""
|
|
179
|
+
Assess IR-5 (Incident Monitoring) compliance.
|
|
180
|
+
|
|
181
|
+
GuardDuty's active monitoring satisfies IR-5 requirements by providing continuous
|
|
182
|
+
security monitoring and incident detection capabilities. The presence of GuardDuty
|
|
183
|
+
itself, along with this integration's tracking of findings, fulfills the control.
|
|
184
|
+
|
|
185
|
+
:param Dict guardduty_data: GuardDuty data
|
|
186
|
+
:return: Compliance result (always PASS when GuardDuty is enabled)
|
|
187
|
+
:rtype: str
|
|
188
|
+
"""
|
|
189
|
+
findings = guardduty_data.get("Findings", [])
|
|
190
|
+
|
|
191
|
+
# GuardDuty being active satisfies IR-5 (Incident Monitoring)
|
|
192
|
+
if findings:
|
|
193
|
+
logger.debug(f"GuardDuty PASSES IR-5: {len(findings)} findings tracked for incident monitoring")
|
|
194
|
+
else:
|
|
195
|
+
logger.debug("GuardDuty PASSES IR-5: GuardDuty monitoring active, no incidents detected")
|
|
196
|
+
|
|
197
|
+
return "PASS"
|
|
198
|
+
|
|
199
|
+
def _assess_si3(self, guardduty_data: Dict) -> str:
|
|
200
|
+
"""
|
|
201
|
+
Assess SI-3 (Malicious Code Protection) compliance.
|
|
202
|
+
|
|
203
|
+
:param Dict guardduty_data: GuardDuty data
|
|
204
|
+
:return: Compliance result (PASS/FAIL)
|
|
205
|
+
:rtype: str
|
|
206
|
+
"""
|
|
207
|
+
detectors = guardduty_data.get("Detectors", [])
|
|
208
|
+
findings = guardduty_data.get("Findings", [])
|
|
209
|
+
|
|
210
|
+
# Check if GuardDuty is enabled (provides malware detection)
|
|
211
|
+
if not detectors or all(d.get("Status") != "ENABLED" for d in detectors):
|
|
212
|
+
logger.debug("GuardDuty FAILS SI-3: No enabled detectors for malware protection")
|
|
213
|
+
return "FAIL"
|
|
214
|
+
|
|
215
|
+
# Check for malware-related findings
|
|
216
|
+
malware_findings = [f for f in findings if self._is_malware_finding(f)]
|
|
217
|
+
|
|
218
|
+
if malware_findings:
|
|
219
|
+
logger.warning(
|
|
220
|
+
f"GuardDuty malware findings detected: {len(malware_findings)} "
|
|
221
|
+
"potential malware activities (requires remediation)"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
logger.debug("GuardDuty PASSES SI-3: Malware protection monitoring enabled")
|
|
225
|
+
return "PASS"
|
|
226
|
+
|
|
227
|
+
def _assess_ra5(self, guardduty_data: Dict) -> str:
|
|
228
|
+
"""
|
|
229
|
+
Assess RA-5 (Vulnerability Monitoring) compliance.
|
|
230
|
+
|
|
231
|
+
:param Dict guardduty_data: GuardDuty data
|
|
232
|
+
:return: Compliance result (PASS/FAIL)
|
|
233
|
+
:rtype: str
|
|
234
|
+
"""
|
|
235
|
+
detectors = guardduty_data.get("Detectors", [])
|
|
236
|
+
|
|
237
|
+
# Check if threat intelligence is enabled
|
|
238
|
+
threat_intel_enabled = any(
|
|
239
|
+
d.get("DataSources", {}).get("S3Logs", {}).get("Status") == "ENABLED" for d in detectors
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if not threat_intel_enabled:
|
|
243
|
+
logger.debug("GuardDuty FAILS RA-5: Threat intelligence data sources not fully enabled")
|
|
244
|
+
return "FAIL"
|
|
245
|
+
|
|
246
|
+
logger.debug("GuardDuty PASSES RA-5: Threat intelligence and vulnerability monitoring active")
|
|
247
|
+
return "PASS"
|
|
248
|
+
|
|
249
|
+
def _get_severity_level(self, severity_score: float) -> str:
|
|
250
|
+
"""
|
|
251
|
+
Map GuardDuty severity score to severity level.
|
|
252
|
+
|
|
253
|
+
:param float severity_score: Severity score (0-10)
|
|
254
|
+
:return: Severity level (LOW, MEDIUM, HIGH, CRITICAL)
|
|
255
|
+
:rtype: str
|
|
256
|
+
"""
|
|
257
|
+
for level, (min_score, max_score) in SEVERITY_MAPPING.items():
|
|
258
|
+
if min_score <= severity_score <= max_score:
|
|
259
|
+
return level
|
|
260
|
+
return "LOW"
|
|
261
|
+
|
|
262
|
+
def _is_malware_finding(self, finding: Dict) -> bool:
|
|
263
|
+
"""
|
|
264
|
+
Check if finding is related to malware.
|
|
265
|
+
|
|
266
|
+
:param Dict finding: GuardDuty finding
|
|
267
|
+
:return: True if malware-related
|
|
268
|
+
:rtype: bool
|
|
269
|
+
"""
|
|
270
|
+
finding_type = finding.get("Type", "")
|
|
271
|
+
malware_indicators = [
|
|
272
|
+
"Trojan",
|
|
273
|
+
"Backdoor",
|
|
274
|
+
"CryptoCurrency",
|
|
275
|
+
"Malware",
|
|
276
|
+
"Bitcoin",
|
|
277
|
+
"CryptoCoin",
|
|
278
|
+
]
|
|
279
|
+
return any(indicator in finding_type for indicator in malware_indicators)
|
|
280
|
+
|
|
281
|
+
def has_cve_reference(self, finding: Dict) -> bool:
|
|
282
|
+
"""
|
|
283
|
+
Check if GuardDuty finding references a CVE.
|
|
284
|
+
|
|
285
|
+
:param Dict finding: GuardDuty finding
|
|
286
|
+
:return: True if CVE referenced
|
|
287
|
+
:rtype: bool
|
|
288
|
+
"""
|
|
289
|
+
# Check finding description and title for CVE patterns
|
|
290
|
+
description = finding.get("Description", "")
|
|
291
|
+
title = finding.get("Title", "")
|
|
292
|
+
service = finding.get("Service", {})
|
|
293
|
+
additional_info = service.get("AdditionalInfo", {})
|
|
294
|
+
|
|
295
|
+
# CVE pattern: CVE-YYYY-NNNNN
|
|
296
|
+
text_to_search = f"{description} {title} {str(additional_info)}"
|
|
297
|
+
return "CVE-" in text_to_search.upper()
|
|
298
|
+
|
|
299
|
+
def extract_cves_from_finding(self, finding: Dict) -> List[str]:
|
|
300
|
+
"""
|
|
301
|
+
Extract CVE IDs from GuardDuty finding.
|
|
302
|
+
|
|
303
|
+
:param Dict finding: GuardDuty finding
|
|
304
|
+
:return: List of CVE IDs
|
|
305
|
+
:rtype: List[str]
|
|
306
|
+
"""
|
|
307
|
+
import re
|
|
308
|
+
|
|
309
|
+
cves = []
|
|
310
|
+
description = finding.get("Description", "")
|
|
311
|
+
title = finding.get("Title", "")
|
|
312
|
+
service = finding.get("Service", {})
|
|
313
|
+
additional_info = str(service.get("AdditionalInfo", {}))
|
|
314
|
+
|
|
315
|
+
text_to_search = f"{description} {title} {additional_info}"
|
|
316
|
+
|
|
317
|
+
# CVE pattern: CVE-YYYY-NNNNN
|
|
318
|
+
cve_pattern = r"CVE-\d{4}-\d{4,7}"
|
|
319
|
+
matches = re.findall(cve_pattern, text_to_search, re.IGNORECASE)
|
|
320
|
+
|
|
321
|
+
cves = list({match.upper() for match in matches})
|
|
322
|
+
return cves
|
|
323
|
+
|
|
324
|
+
def get_control_description(self, control_id: str) -> Optional[str]:
|
|
325
|
+
"""Get human-readable description for a control."""
|
|
326
|
+
control_data = self.mappings.get(control_id)
|
|
327
|
+
if control_data:
|
|
328
|
+
return f"{control_data.get('name')}: {control_data.get('description', '')}"
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
def get_mapped_controls(self) -> List[str]:
|
|
332
|
+
"""Get list of all control IDs mapped for this framework."""
|
|
333
|
+
return list(self.mappings.keys())
|
|
334
|
+
|
|
335
|
+
def get_check_details(self, control_id: str) -> Optional[Dict]:
|
|
336
|
+
"""Get detailed check criteria for a control."""
|
|
337
|
+
control_data = self.mappings.get(control_id)
|
|
338
|
+
if control_data:
|
|
339
|
+
return control_data.get("checks", {})
|
|
340
|
+
return None
|