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,432 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Integrates CSAM into RegScale"""
|
|
4
|
+
|
|
5
|
+
# standard python imports
|
|
6
|
+
import logging
|
|
7
|
+
from typing import List, Optional, Tuple, Any, Dict
|
|
8
|
+
from rich.progress import track
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from regscale.core.app.application import Application
|
|
11
|
+
from regscale.models.regscale_models import (
|
|
12
|
+
Catalog,
|
|
13
|
+
ControlImplementation,
|
|
14
|
+
InheritedControl,
|
|
15
|
+
Inheritance,
|
|
16
|
+
SecurityControl,
|
|
17
|
+
SecurityPlan,
|
|
18
|
+
)
|
|
19
|
+
from regscale.integrations.control_matcher import ControlMatcher
|
|
20
|
+
from regscale.models.regscale_models.form_field_value import FormFieldValue
|
|
21
|
+
from regscale.integrations.public.csam.csam_common import (
|
|
22
|
+
retrieve_from_csam,
|
|
23
|
+
retrieve_ssps_custom_form_map,
|
|
24
|
+
CSAM_FIELD_NAME,
|
|
25
|
+
SSP_BASIC_TAB,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
FULLY_IMPLEMENTED = "Fully Implemented"
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger("regscale")
|
|
31
|
+
console = Console()
|
|
32
|
+
|
|
33
|
+
####################################################################################################
|
|
34
|
+
#
|
|
35
|
+
# IMPORT SSP / POAM FROM DoJ's CSAM GRC
|
|
36
|
+
# CSAM API Docs: https://csam.dhs.gov/CSAM/api/docs/index.html (required PIV)
|
|
37
|
+
#
|
|
38
|
+
####################################################################################################
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def import_csam_controls(import_ids: Optional[List[int]] = None):
|
|
42
|
+
"""
|
|
43
|
+
Import Controls from CSAM
|
|
44
|
+
|
|
45
|
+
:param list import_ids: Filtered list of SSPs
|
|
46
|
+
:return: None
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
# Get existing ssps by CSAM Id
|
|
50
|
+
custom_fields_basic_map = FormFieldValue.check_custom_fields([CSAM_FIELD_NAME], "securityplans", SSP_BASIC_TAB)
|
|
51
|
+
ssp_map = retrieve_ssps_custom_form_map(
|
|
52
|
+
tab_name=SSP_BASIC_TAB, field_form_id=custom_fields_basic_map[CSAM_FIELD_NAME]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
plans = import_ids if import_ids else list(ssp_map.keys())
|
|
56
|
+
|
|
57
|
+
# Find the Catalogs
|
|
58
|
+
rev5_catalog_id, rev4_catalog_id = get_catalogs()
|
|
59
|
+
|
|
60
|
+
# Get the list of controls for each catalog
|
|
61
|
+
rev5_controls = SecurityControl.get_list_by_catalog(catalog_id=rev5_catalog_id)
|
|
62
|
+
rev4_controls = SecurityControl.get_list_by_catalog(catalog_id=rev4_catalog_id)
|
|
63
|
+
|
|
64
|
+
control_implementations = []
|
|
65
|
+
for regscale_ssp_id in plans:
|
|
66
|
+
results = []
|
|
67
|
+
system_id = ssp_map.get(regscale_ssp_id)
|
|
68
|
+
|
|
69
|
+
# Get the Implementation for AC-1
|
|
70
|
+
# Check the controlSet
|
|
71
|
+
# Match the catalog
|
|
72
|
+
imp = retrieve_from_csam(
|
|
73
|
+
csam_endpoint=f"/CSAM/api/v1/systems/{system_id}/controls/AC-1",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Get the controls
|
|
77
|
+
if imp[0].get("controlSet") in ["NIST 800-53 Rev4", "NIST 800-53 Rev5"]:
|
|
78
|
+
results = retrieve_controls(
|
|
79
|
+
csam_id=system_id,
|
|
80
|
+
controls=rev4_controls if imp[0].get("controlSet") == "NIST 800-53 Rev4" else rev5_controls,
|
|
81
|
+
regscale_id=regscale_ssp_id,
|
|
82
|
+
)
|
|
83
|
+
else:
|
|
84
|
+
logger.warning(
|
|
85
|
+
f"System framework {imp.get('controlSet')} \
|
|
86
|
+
for system {system_id} is not supported"
|
|
87
|
+
)
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
if not results:
|
|
91
|
+
logger.warning(f"No controls found for system id: {system_id}")
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
# Build the controls
|
|
95
|
+
control_implementations = build_implementations(results=results, regscale_id=regscale_ssp_id)
|
|
96
|
+
|
|
97
|
+
# Save the control implementations
|
|
98
|
+
for index in track(
|
|
99
|
+
range(len(control_implementations)),
|
|
100
|
+
description=f"Saving {len(control_implementations)} control implementations...",
|
|
101
|
+
):
|
|
102
|
+
control_implementation = control_implementations[index]
|
|
103
|
+
control_implementation.create() if control_implementation.id == 0 else control_implementation.save()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def build_implementations(results: list, regscale_id: int) -> list:
|
|
107
|
+
"""
|
|
108
|
+
Build out the control implementations
|
|
109
|
+
from the results returned from CSAM
|
|
110
|
+
|
|
111
|
+
:param list results: records from CSAM
|
|
112
|
+
:param int regscale_id: RegScale SSP Id
|
|
113
|
+
:return: list of ControlImplementation objects
|
|
114
|
+
:return_type: list
|
|
115
|
+
"""
|
|
116
|
+
matcher = ControlMatcher()
|
|
117
|
+
control_implementations = []
|
|
118
|
+
|
|
119
|
+
# Loop through the results and create or update the controls
|
|
120
|
+
for index in track(
|
|
121
|
+
range(len(results)),
|
|
122
|
+
description=f"Importing {len(results)} controls for system id: {regscale_id}...",
|
|
123
|
+
):
|
|
124
|
+
result = results[index]
|
|
125
|
+
|
|
126
|
+
# Skip if not implemented
|
|
127
|
+
if not result["statedImplementationStatus"]:
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
# Match existing controlImplementation
|
|
131
|
+
implementation = matcher.find_control_implementation(
|
|
132
|
+
control_id=result["controlId"], parent_id=regscale_id, parent_module="securityplans"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Set values
|
|
136
|
+
status_lst = [FULLY_IMPLEMENTED if result["statedImplementationStatus"] == "Implemented" else "Not Implemented"]
|
|
137
|
+
|
|
138
|
+
status = FULLY_IMPLEMENTED if result["statedImplementationStatus"] == "Implemented" else "Not Implemented"
|
|
139
|
+
|
|
140
|
+
responsibility = (
|
|
141
|
+
result["applicability"]
|
|
142
|
+
if result["applicability"] in ["Hybrid", "Inherited"]
|
|
143
|
+
else "Provider (System Specific)"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
impl = result["implementationStatement"]
|
|
147
|
+
|
|
148
|
+
if implementation:
|
|
149
|
+
# Update it
|
|
150
|
+
implementation.status_lst = status_lst
|
|
151
|
+
implementation.status = status
|
|
152
|
+
implementation.responsibility = responsibility
|
|
153
|
+
implementation.controlSource = "Baseline"
|
|
154
|
+
implementation.implementation = impl
|
|
155
|
+
else:
|
|
156
|
+
# Build it from the catalog
|
|
157
|
+
implementation = ControlImplementation(
|
|
158
|
+
status_lst=status_lst,
|
|
159
|
+
status=status,
|
|
160
|
+
parentId=regscale_id,
|
|
161
|
+
parentModule="securityplans",
|
|
162
|
+
controlID=result["controlID"],
|
|
163
|
+
responsibility=responsibility,
|
|
164
|
+
controlSource="Baseline",
|
|
165
|
+
implementation=impl,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
control_implementations.append(implementation)
|
|
169
|
+
|
|
170
|
+
return control_implementations
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def retrieve_controls(csam_id: int, controls: list, regscale_id: int) -> list:
|
|
174
|
+
"""
|
|
175
|
+
Takes a system id and list of controls
|
|
176
|
+
returns a list of implmentations for
|
|
177
|
+
that system id and framework
|
|
178
|
+
|
|
179
|
+
:param int system_id: CSAM system id
|
|
180
|
+
:param str framework: Framework name
|
|
181
|
+
:param list controls: list of possible controls
|
|
182
|
+
:param int regscale_id: RegScale SSP Id
|
|
183
|
+
:return: list of control implementations
|
|
184
|
+
:return_type: list
|
|
185
|
+
"""
|
|
186
|
+
imps = []
|
|
187
|
+
# Loop through the controls and get the implementations
|
|
188
|
+
for index in track(
|
|
189
|
+
range(len(controls)),
|
|
190
|
+
description=f"Retrieving implementations for system id: {csam_id}...",
|
|
191
|
+
):
|
|
192
|
+
control = controls[index]
|
|
193
|
+
implementations = retrieve_from_csam(
|
|
194
|
+
csam_endpoint=f"/CSAM/api/v1/systems/{csam_id}/controls/{control.controlId}",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if len(implementations) == 0:
|
|
198
|
+
logger.debug(f"No implementations found for control {control.controlId} in system id: {csam_id}")
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
# Add the RegScale SSP Id and controlID to the implementation
|
|
202
|
+
for impl in implementations:
|
|
203
|
+
if "NotApplicable" in impl["applicability"]:
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
impl["securityPlanId"] = regscale_id
|
|
207
|
+
impl["controlID"] = control.id
|
|
208
|
+
imps.append(impl)
|
|
209
|
+
return imps
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def set_inheritable(regscale_id: int):
|
|
213
|
+
"""
|
|
214
|
+
Given a RegScale SSP Id
|
|
215
|
+
Sets the inheritable flag on all control implementations
|
|
216
|
+
|
|
217
|
+
:param int regscale_id: id of Security Plan
|
|
218
|
+
:return: None
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
# Get list of existing controlimplementations
|
|
222
|
+
implementations = ControlImplementation.get_list_by_parent(regscale_id=regscale_id, regscale_module="securityplans")
|
|
223
|
+
|
|
224
|
+
for index in track(
|
|
225
|
+
range(len(implementations)),
|
|
226
|
+
description="Setting controls Inheritable...",
|
|
227
|
+
):
|
|
228
|
+
implementation = implementations[index]
|
|
229
|
+
imp = ControlImplementation.get_object(object_id=implementation["id"])
|
|
230
|
+
if imp.status in [FULLY_IMPLEMENTED, "Partially Implemented", "In Remediation"]:
|
|
231
|
+
imp.inheritable = True
|
|
232
|
+
imp.save()
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def trim_inheritances(inheritances=List[Dict]) -> List[Dict]:
|
|
236
|
+
# Trim out the records that aren't inherited
|
|
237
|
+
# Trim down to one record per Control (vice "deterineifs")
|
|
238
|
+
|
|
239
|
+
unique_controls = []
|
|
240
|
+
new_list = []
|
|
241
|
+
|
|
242
|
+
for inheritance in inheritances:
|
|
243
|
+
if inheritance.get("isInherited") is False:
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
if inheritance.get("controlId") in unique_controls:
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
unique_controls.append(inheritance.get("controlId"))
|
|
250
|
+
new_list.append(inheritance)
|
|
251
|
+
|
|
252
|
+
return new_list
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def process_inheritances(
|
|
256
|
+
inheritances: List[Dict[str, Any]],
|
|
257
|
+
ssp: SecurityPlan,
|
|
258
|
+
ssp_map: Dict[str, int],
|
|
259
|
+
imp_map: Dict[str, int],
|
|
260
|
+
linked_ssps: List[SecurityPlan],
|
|
261
|
+
):
|
|
262
|
+
|
|
263
|
+
matcher = ControlMatcher()
|
|
264
|
+
for inheritance in inheritances:
|
|
265
|
+
# If not inherited, skip
|
|
266
|
+
if inheritance.get("isInherited") is False:
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
# Check if the control exists in plan
|
|
270
|
+
control_id = matcher.find_control_implementation(
|
|
271
|
+
control_id=inheritance.get("controlId"), parent_id=ssp.id, parent_module="securityplans"
|
|
272
|
+
)
|
|
273
|
+
if control_id not in imp_map:
|
|
274
|
+
logger.debug(f"Control {control_id} not found in RegScale for SSP {ssp.systemName} (ID: {ssp.id})")
|
|
275
|
+
continue
|
|
276
|
+
|
|
277
|
+
# Find the baseControl in RegScale
|
|
278
|
+
# Find the SSP
|
|
279
|
+
base_ssp = ssp_map.get(inheritance.get("offeringSystemName"))
|
|
280
|
+
if not base_ssp:
|
|
281
|
+
logger.debug(f"Base SSP {inheritance.get('offeringSystemName')} not found in RegScale, skipping")
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
# Find the source control
|
|
285
|
+
base_control_id = matcher.find_control_implementation(
|
|
286
|
+
control_id=inheritance.get("controlId"), parent_id=base_ssp, parent_module="securityplans"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
if not base_control_id:
|
|
290
|
+
logger.debug(f"Control not found in Base SSP: {inheritance.get('offeringSystemName')}, skipping")
|
|
291
|
+
continue
|
|
292
|
+
|
|
293
|
+
# Create or update the inheritance record
|
|
294
|
+
# Add the parent if not already linked
|
|
295
|
+
if base_ssp not in linked_ssps:
|
|
296
|
+
linked_ssps.append(base_ssp)
|
|
297
|
+
|
|
298
|
+
# Create the records
|
|
299
|
+
create_inheritance(
|
|
300
|
+
parent_id=ssp.id,
|
|
301
|
+
parent_module="securityplans",
|
|
302
|
+
hybrid=inheritance.get("isHybrid", True),
|
|
303
|
+
base_id=base_ssp,
|
|
304
|
+
control_id=imp_map[control_id],
|
|
305
|
+
base_control_id=base_control_id,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# Create the Inheritance Record(s)
|
|
309
|
+
for inheritance_ssp in linked_ssps:
|
|
310
|
+
create_inheritance_linage(
|
|
311
|
+
parent_id=ssp.id,
|
|
312
|
+
parent_module="securityplans",
|
|
313
|
+
base_id=inheritance_ssp,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def create_inheritance(
|
|
318
|
+
parent_id: int, parent_module: str, base_id: int, hybrid: bool, control_id: int, base_control_id: int
|
|
319
|
+
):
|
|
320
|
+
"""
|
|
321
|
+
Creates the records for inheritance
|
|
322
|
+
|
|
323
|
+
:param int parent_id: Id of inheriting record
|
|
324
|
+
:param str parent_module: Module of inheriting record
|
|
325
|
+
:param int base_id: Id of inherited record
|
|
326
|
+
:param bool hybrid: Is the control hybrid
|
|
327
|
+
:param int control_id: Id of inheriting control
|
|
328
|
+
:param int base_control_id: Id of inherited control
|
|
329
|
+
:return: None
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
# Update the control implementation
|
|
333
|
+
control_impl = ControlImplementation.get_object(object_id=control_id)
|
|
334
|
+
if control_impl:
|
|
335
|
+
control_impl.bInherited = True
|
|
336
|
+
control_impl.responsibility = "Hybrid" if hybrid else "Inherited"
|
|
337
|
+
control_impl.inheritedControlId = base_control_id
|
|
338
|
+
control_impl.inheritedSecurityPlanId = base_id
|
|
339
|
+
control_impl.save()
|
|
340
|
+
|
|
341
|
+
# Check if the Inherited Control already exists
|
|
342
|
+
existing = InheritedControl.get_all_by_control(control_id=control_id)
|
|
343
|
+
for exists in existing:
|
|
344
|
+
if exists["inheritedControlId"] == base_control_id:
|
|
345
|
+
return
|
|
346
|
+
|
|
347
|
+
InheritedControl(
|
|
348
|
+
parentId=parent_id, parentModule=parent_module, baseControlId=control_id, inheritedControlId=base_control_id
|
|
349
|
+
).create()
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def create_inheritance_linage(parent_id: int, parent_module: str, base_id: int):
|
|
353
|
+
"""
|
|
354
|
+
Creates a RegScale Inheritance Record
|
|
355
|
+
|
|
356
|
+
:param int parent_id: Id of inheriting record
|
|
357
|
+
:param str parent_module: Module of inheriting record
|
|
358
|
+
:param int base_control_id: Id of inherited control
|
|
359
|
+
:return: None
|
|
360
|
+
"""
|
|
361
|
+
# Check if the Inheritance already exists
|
|
362
|
+
existing = Inheritance.get_all_by_parent(parent_id=parent_id, parent_module=parent_module)
|
|
363
|
+
for exists in existing:
|
|
364
|
+
if exists.planId == base_id:
|
|
365
|
+
return
|
|
366
|
+
|
|
367
|
+
# Update Lineage (no way to update.. only create)
|
|
368
|
+
Inheritance(recordId=parent_id, recordModule=parent_module, planId=base_id).create()
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def import_csam_inheritance(import_ids: Optional[List[int]] = None):
|
|
372
|
+
"""
|
|
373
|
+
Import control inheritance from CSAM
|
|
374
|
+
|
|
375
|
+
:param list import_ids: List of SSPs to import
|
|
376
|
+
:return: None
|
|
377
|
+
"""
|
|
378
|
+
|
|
379
|
+
# Get list of existing SSPs in RegScale
|
|
380
|
+
existing_ssps = SecurityPlan.get_ssp_list()
|
|
381
|
+
ssp_map = {ssp["title"]: ssp["id"] for ssp in existing_ssps}
|
|
382
|
+
|
|
383
|
+
if not import_ids:
|
|
384
|
+
import_ids = [ssp["id"] for ssp in existing_ssps]
|
|
385
|
+
|
|
386
|
+
# Get Inheritance data from CSAM
|
|
387
|
+
for index in track(
|
|
388
|
+
range(len(import_ids)),
|
|
389
|
+
description=f"Importing inheritance for {len(import_ids)} Systems...",
|
|
390
|
+
):
|
|
391
|
+
ssp = SecurityPlan.get_object(object_id=import_ids[index])
|
|
392
|
+
linked_ssps = []
|
|
393
|
+
# Get the inheritance data from CSAM
|
|
394
|
+
|
|
395
|
+
inheritances = retrieve_from_csam(
|
|
396
|
+
csam_endpoint=f"/CSAM/api/v1/systems/{ssp.otherIdentifier}/inheritedcontrols",
|
|
397
|
+
)
|
|
398
|
+
if not inheritances:
|
|
399
|
+
logger.debug(f"No inheritance data found for SSP {ssp.systemName} (ID: {ssp.id})")
|
|
400
|
+
continue
|
|
401
|
+
# Process each inheritance record
|
|
402
|
+
imp_map = ControlImplementation.get_control_label_map_by_plan(plan_id=ssp.id)
|
|
403
|
+
|
|
404
|
+
inheritances = trim_inheritances(inheritances=inheritances)
|
|
405
|
+
|
|
406
|
+
process_inheritances(
|
|
407
|
+
inheritances=inheritances, ssp=ssp, ssp_map=ssp_map, imp_map=imp_map, linked_ssps=linked_ssps
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def get_catalogs() -> Tuple[Optional[int], Optional[int]]:
|
|
412
|
+
"""
|
|
413
|
+
Get the catalog ids for NIST SP 800-53 Rev 5 and Rev 4
|
|
414
|
+
|
|
415
|
+
:return: tuple of catalog ids
|
|
416
|
+
:return_type: Tuple[Optional[int], Optional[int]]
|
|
417
|
+
"""
|
|
418
|
+
# Find the Catalogs
|
|
419
|
+
# Use the init.yaml values, otherwise search for the catalogs by guid
|
|
420
|
+
app = Application()
|
|
421
|
+
catalogs = app.config.get("csamFrameworkCatalog")
|
|
422
|
+
rev5_catalog = Catalog.find_by_guid("b0c40faa-fda4-4ed3-83df-368908d9e9b2") # NIST SP 800-53 Rev 5
|
|
423
|
+
rev4_catalog = Catalog.find_by_guid("02158108-e491-49de-b9a8-3cb1cb8197dd") # NIST SP 800-53 Rev 4
|
|
424
|
+
|
|
425
|
+
if catalogs:
|
|
426
|
+
rev5_catalog_id = catalogs.get("800-53r5")
|
|
427
|
+
rev4_catalog_id = catalogs.get("800-53r4")
|
|
428
|
+
elif rev5_catalog and rev4_catalog:
|
|
429
|
+
rev5_catalog_id = rev5_catalog.id
|
|
430
|
+
rev4_catalog_id = rev4_catalog.id
|
|
431
|
+
|
|
432
|
+
return rev5_catalog_id, rev4_catalog_id
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Integrates CSAM into RegScale"""
|
|
4
|
+
|
|
5
|
+
# standard python imports
|
|
6
|
+
import logging
|
|
7
|
+
from typing import List
|
|
8
|
+
from rich.progress import track
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from regscale.core.app.api import Api
|
|
11
|
+
from regscale.core.app.application import Application
|
|
12
|
+
from regscale.core.utils.date import format_to_regscale_iso, date_obj
|
|
13
|
+
from regscale.core.app.utils.app_utils import error_and_exit, filter_list
|
|
14
|
+
from regscale.core.app.utils.parser_utils import safe_date_str
|
|
15
|
+
from regscale.models.regscale_models import (
|
|
16
|
+
Issue,
|
|
17
|
+
SecurityControl,
|
|
18
|
+
SecurityPlan,
|
|
19
|
+
User,
|
|
20
|
+
)
|
|
21
|
+
from regscale.models.regscale_models.regscale_model import RegScaleModel
|
|
22
|
+
from regscale.integrations.control_matcher import ControlMatcher
|
|
23
|
+
from regscale.models.regscale_models.form_field_value import FormFieldValue
|
|
24
|
+
from regscale.integrations.public.csam.csam_common import (
|
|
25
|
+
retrieve_ssps_custom_form_map,
|
|
26
|
+
retrieve_from_csam,
|
|
27
|
+
FISMA_FIELD_NAME,
|
|
28
|
+
CSAM_FIELD_NAME,
|
|
29
|
+
SSP_BASIC_TAB,
|
|
30
|
+
SYSTEM_ID,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
POAM_ID = "POAM Id"
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger("regscale")
|
|
36
|
+
console = Console()
|
|
37
|
+
|
|
38
|
+
####################################################################################################
|
|
39
|
+
#
|
|
40
|
+
# IMPORT SSP / POAM FROM DoJ's CSAM GRC
|
|
41
|
+
# CSAM API Docs: https://csam.dhs.gov/CSAM/api/docs/index.html (required PIV)
|
|
42
|
+
#
|
|
43
|
+
####################################################################################################
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def import_csam_poams():
|
|
47
|
+
# Check Custom Fields
|
|
48
|
+
custom_fields_basic_map = FormFieldValue.check_custom_fields(
|
|
49
|
+
fields_list=[FISMA_FIELD_NAME, CSAM_FIELD_NAME], module_name="securityplans", tab_name=SSP_BASIC_TAB
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Get the SSPs
|
|
53
|
+
ssp_map = retrieve_ssps_custom_form_map(
|
|
54
|
+
tab_name=SSP_BASIC_TAB, field_form_id=custom_fields_basic_map[CSAM_FIELD_NAME]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Get a list of users and create a map to id
|
|
58
|
+
users = User.get_all()
|
|
59
|
+
user_map = {user.userName: user.id for user in users}
|
|
60
|
+
|
|
61
|
+
# Grab the data from CSAM
|
|
62
|
+
results = retrieve_from_csam(csam_endpoint="/CSAM/api/v1/reports/POAM_Details_Report_CBP")
|
|
63
|
+
|
|
64
|
+
# Parse the results
|
|
65
|
+
poam_list = []
|
|
66
|
+
for index in track(
|
|
67
|
+
range(len(results)),
|
|
68
|
+
description=f"Importing {len(results)} POA&Ms...",
|
|
69
|
+
):
|
|
70
|
+
result = results[index]
|
|
71
|
+
|
|
72
|
+
# Get the existing SSP:
|
|
73
|
+
ssp_id = ssp_map.get(str(result[SYSTEM_ID]))
|
|
74
|
+
if not ssp_id:
|
|
75
|
+
logger.error(
|
|
76
|
+
f"A RegScale Security Plan does not exist for CSAM id: {result[SYSTEM_ID]}\
|
|
77
|
+
create or import the Security Plan prior to importing POA&Ms"
|
|
78
|
+
)
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
# Check if the POAM exists:
|
|
82
|
+
existing_issue = Issue.find_by_other_identifier(result[POAM_ID])
|
|
83
|
+
if existing_issue:
|
|
84
|
+
new_issue = existing_issue
|
|
85
|
+
else:
|
|
86
|
+
new_issue = Issue()
|
|
87
|
+
|
|
88
|
+
# Update the issue
|
|
89
|
+
new_issue.isPoam = True
|
|
90
|
+
new_issue.parentId = ssp_id
|
|
91
|
+
new_issue.parentModule = "securityplans"
|
|
92
|
+
new_issue.otherIdentifier = result[POAM_ID]
|
|
93
|
+
new_issue.title = result["POAM Title"]
|
|
94
|
+
new_issue.affectedControls = result["Controls"]
|
|
95
|
+
new_issue.securityPlanId = ssp_id
|
|
96
|
+
new_issue.identification = "Vulnerability Assessment"
|
|
97
|
+
new_issue.description = result["Detailed Weakness Description"]
|
|
98
|
+
new_issue.poamComments = f"{result['Weakness Comments']}\n \
|
|
99
|
+
{result['POA&M Delayed Comments']}\n \
|
|
100
|
+
{result['POA&M Comments']}"
|
|
101
|
+
new_issue.dateFirstDetected = safe_date_str(result["Create Date"])
|
|
102
|
+
new_issue.dueDate = safe_date_str(result["Planned Finish Date"])
|
|
103
|
+
# Need to convert cost to a int
|
|
104
|
+
# new_issue.costEstimate = result['Cost']
|
|
105
|
+
new_issue.issueOwnerId = (
|
|
106
|
+
user_map.get(result["Email"]) if user_map.get(result["Email"]) else RegScaleModel.get_user_id()
|
|
107
|
+
)
|
|
108
|
+
# Update with IssueSeverity String
|
|
109
|
+
new_issue.severityLevel = result["Severity"]
|
|
110
|
+
# Update with IssueStatus String
|
|
111
|
+
new_issue.status = result["Status"]
|
|
112
|
+
|
|
113
|
+
poam_list.append(new_issue)
|
|
114
|
+
|
|
115
|
+
for index in track(
|
|
116
|
+
range(len(poam_list)),
|
|
117
|
+
description=f"Updating RegScale with {len(poam_list)} POA&Ms...",
|
|
118
|
+
):
|
|
119
|
+
poam = poam_list[index]
|
|
120
|
+
if poam.id == 0:
|
|
121
|
+
poam.create()
|
|
122
|
+
else:
|
|
123
|
+
poam.save()
|
|
124
|
+
logger.info(f"Added or updated {len(poam_list)} POA&Ms in RegScale")
|
|
@@ -9,8 +9,9 @@ from typing import Literal, Optional
|
|
|
9
9
|
import click
|
|
10
10
|
from dateutil.relativedelta import relativedelta
|
|
11
11
|
|
|
12
|
-
from regscale.core.app.utils.regscale_utils import check_module_id
|
|
12
|
+
from regscale.core.app.utils.regscale_utils import check_module_id, error_and_exit
|
|
13
13
|
from regscale.models import regscale_id, regscale_module
|
|
14
|
+
from regscale.models.regscale_models import Asset
|
|
14
15
|
|
|
15
16
|
logger = logging.getLogger("regscale")
|
|
16
17
|
|
|
@@ -20,6 +21,59 @@ def fedramp():
|
|
|
20
21
|
"""Performs bulk processing of FedRAMP files (Upload trusted data only)."""
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
@fedramp.command(context_settings={"show_default": True})
|
|
25
|
+
@click.option(
|
|
26
|
+
"--ssp_id",
|
|
27
|
+
"-s",
|
|
28
|
+
type=click.STRING,
|
|
29
|
+
required=True,
|
|
30
|
+
prompt="Enter the SSP ID to export POAMs from",
|
|
31
|
+
help="The RegScale SSP ID to export POAMs from",
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--output_file",
|
|
35
|
+
"-o",
|
|
36
|
+
type=click.STRING,
|
|
37
|
+
required=True,
|
|
38
|
+
prompt="Enter the output file path (xlsx)",
|
|
39
|
+
help="The output file path for the POAM export (xlsx format)",
|
|
40
|
+
)
|
|
41
|
+
@click.option(
|
|
42
|
+
"--template_path",
|
|
43
|
+
"-t",
|
|
44
|
+
type=click.Path(exists=True, dir_okay=False, file_okay=True),
|
|
45
|
+
required=False,
|
|
46
|
+
help="Path to the FedRAMP POAM template Excel file (defaults to ./templates/FedRAMP-POAM-Template.xlsx)",
|
|
47
|
+
)
|
|
48
|
+
@click.option(
|
|
49
|
+
"--point_of_contact",
|
|
50
|
+
"-p",
|
|
51
|
+
type=click.STRING,
|
|
52
|
+
required=False,
|
|
53
|
+
default="",
|
|
54
|
+
help="Point of Contact name for POAMs (defaults to empty string)",
|
|
55
|
+
)
|
|
56
|
+
def export_poam_v5(ssp_id: str, output_file: str, template_path: Optional[click.Path], point_of_contact: str):
|
|
57
|
+
"""
|
|
58
|
+
Export FedRAMP Rev 5 POAM Excel file with advanced formatting.
|
|
59
|
+
|
|
60
|
+
This export includes:
|
|
61
|
+
- Dynamic POAM ID generation based on source file paths
|
|
62
|
+
- KEV date determination from CISA KEV catalog
|
|
63
|
+
- Deviation status mapping (Approved/Pending/Rejected)
|
|
64
|
+
- Custom milestone and comment generation
|
|
65
|
+
- Excel formatting for Rev 5 template
|
|
66
|
+
- Configurable Point of Contact
|
|
67
|
+
"""
|
|
68
|
+
from pathlib import Path
|
|
69
|
+
from regscale.integrations.public.fedramp.poam_export_v5 import export_poam_v5 as export_func
|
|
70
|
+
|
|
71
|
+
logger.info(f"Exporting FedRAMP Rev 5 POAM for SSP {ssp_id}")
|
|
72
|
+
|
|
73
|
+
template = Path(template_path) if template_path else None
|
|
74
|
+
export_func(ssp_id=ssp_id, output_file=output_file, template_path=template, point_of_contact=point_of_contact)
|
|
75
|
+
|
|
76
|
+
|
|
23
77
|
# FedRAMP Docx Support
|
|
24
78
|
@fedramp.command(context_settings={"show_default": True})
|
|
25
79
|
@click.option(
|
|
@@ -108,7 +162,9 @@ def load_fedramp_docx(
|
|
|
108
162
|
"by using the -p flag."
|
|
109
163
|
)
|
|
110
164
|
|
|
111
|
-
process_fedramp_docx_v5(
|
|
165
|
+
process_fedramp_docx_v5(
|
|
166
|
+
file_path, base_fedramp_profile_id, save_data, add_missing, appendix_a_file_path
|
|
167
|
+
) # type: ignore
|
|
112
168
|
|
|
113
169
|
|
|
114
170
|
@fedramp.command()
|
|
@@ -313,7 +369,7 @@ def import_fedramp_inventory(
|
|
|
313
369
|
type=click.Path(exists=True, dir_okay=False, file_okay=True),
|
|
314
370
|
required=True,
|
|
315
371
|
prompt="Enter the file path containing FedRAMP (.xlsx) POAM workbook to ingest to RegScale.",
|
|
316
|
-
help="RegScale will process and load the FedRAMP POAMs as RegScale issues.",
|
|
372
|
+
help="RegScale will process and load the FedRAMP POAMs (including POA&M Items and Configuration Findings) as RegScale issues.",
|
|
317
373
|
)
|
|
318
374
|
@regscale_id()
|
|
319
375
|
@regscale_module()
|
|
@@ -337,6 +393,8 @@ def import_fedramp_poam_template(
|
|
|
337
393
|
) -> None:
|
|
338
394
|
"""
|
|
339
395
|
Import a FedRamp POA&M document to RegScale issues.
|
|
396
|
+
|
|
397
|
+
Supports both POA&M Items and Configuration Findings tabs.
|
|
340
398
|
"""
|
|
341
399
|
# suppress UserWarnings from openpyxl
|
|
342
400
|
import warnings
|
|
@@ -346,7 +404,17 @@ def import_fedramp_poam_template(
|
|
|
346
404
|
warnings.filterwarnings("ignore", category=UserWarning, module="openpyxl")
|
|
347
405
|
|
|
348
406
|
if not check_module_id(parent_id=regscale_id, parent_module=regscale_module):
|
|
349
|
-
|
|
407
|
+
error_and_exit(f"RegScale ID {regscale_id} is not a valid member of {regscale_module}.")
|
|
408
|
+
|
|
409
|
+
# Check if assets exist before importing POAMs
|
|
410
|
+
existing_assets = Asset.get_all_by_parent(parent_id=regscale_id, parent_module=regscale_module)
|
|
411
|
+
if not existing_assets:
|
|
412
|
+
error_msg = (
|
|
413
|
+
f"No assets found in {regscale_module} #{regscale_id}. "
|
|
414
|
+
"Please import inventory first using 'regscale fedramp import_fedramp_inventory' "
|
|
415
|
+
"before importing POAMs."
|
|
416
|
+
)
|
|
417
|
+
error_and_exit(error_msg)
|
|
350
418
|
|
|
351
419
|
# Initialize the FedRAMP integration
|
|
352
420
|
integration = FedrampPoamIntegration(plan_id=regscale_id, file_path=str(file_path))
|
|
@@ -401,7 +469,7 @@ def import_drf(file_path: click.Path, regscale_id: int, regscale_module: str) ->
|
|
|
401
469
|
warnings.filterwarnings("ignore", category=UserWarning, module="openpyxl")
|
|
402
470
|
|
|
403
471
|
if not check_module_id(parent_id=regscale_id, parent_module=regscale_module):
|
|
404
|
-
|
|
472
|
+
error_and_exit(f"RegScale ID {regscale_id} is not a valid member of {regscale_module}.")
|
|
405
473
|
DRF(file_path=file_path, module_id=regscale_id, module=regscale_module)
|
|
406
474
|
|
|
407
475
|
|
|
@@ -435,7 +503,10 @@ def import_drf(file_path: click.Path, regscale_id: int, regscale_module: str) ->
|
|
|
435
503
|
"--profile_id",
|
|
436
504
|
"-p",
|
|
437
505
|
type=click.INT,
|
|
438
|
-
help=
|
|
506
|
+
help=(
|
|
507
|
+
"The ID number from RegScale of the Profile. (This will generate the control implementations "
|
|
508
|
+
"for a new Security Plan)"
|
|
509
|
+
),
|
|
439
510
|
prompt="Enter RegScale Profile ID",
|
|
440
511
|
required=True,
|
|
441
512
|
)
|