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
|
@@ -203,7 +203,6 @@ class SicuraAPI:
|
|
|
203
203
|
FILTER_TYPE = "filter[type]"
|
|
204
204
|
FILTER_REJECTED = "filter[rejected]"
|
|
205
205
|
FILTER_TASK_ID = "filter[task_id]"
|
|
206
|
-
csrf_token: Optional[str] = None
|
|
207
206
|
|
|
208
207
|
def __init__(self):
|
|
209
208
|
"""
|
|
@@ -246,12 +245,9 @@ class SicuraAPI:
|
|
|
246
245
|
|
|
247
246
|
if data:
|
|
248
247
|
self.session.headers["Content-Type"] = "application/json"
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if self.csrf_token:
|
|
253
|
-
self.session.headers["X-CSRF-TOKEN"] = str(self.csrf_token)
|
|
254
|
-
self.session.headers["auth-token-signature"] = SicuraVariables.sicuraToken
|
|
248
|
+
|
|
249
|
+
# Always set the auth token signature for API authentication
|
|
250
|
+
self.session.headers["auth-token-signature"] = SicuraVariables.sicuraToken
|
|
255
251
|
|
|
256
252
|
try:
|
|
257
253
|
# Use the session object to maintain cookies between requests
|
|
@@ -313,22 +309,6 @@ class SicuraAPI:
|
|
|
313
309
|
logging.error(f"Error validating response: {e}", exc_info=True)
|
|
314
310
|
return None
|
|
315
311
|
|
|
316
|
-
def get_csrf_token(self) -> Optional[AuthResponse]:
|
|
317
|
-
"""
|
|
318
|
-
Get authentication token from Sicura API.
|
|
319
|
-
|
|
320
|
-
:return: Authentication response
|
|
321
|
-
:rtype: Optional[AuthResponse]
|
|
322
|
-
:raises requests.exceptions.RequestException: If the request fails
|
|
323
|
-
"""
|
|
324
|
-
try:
|
|
325
|
-
response = self._make_request("GET", "/auth/token")
|
|
326
|
-
self.csrf_token = response
|
|
327
|
-
return response
|
|
328
|
-
except requests.exceptions.RequestException as e:
|
|
329
|
-
logger.error(f"Error getting authentication token: {e}", exc_info=True)
|
|
330
|
-
raise
|
|
331
|
-
|
|
332
312
|
class Device(SicuraModel):
|
|
333
313
|
"""Model for Sicura device information."""
|
|
334
314
|
|
|
@@ -356,7 +336,7 @@ class SicuraAPI:
|
|
|
356
336
|
try:
|
|
357
337
|
response = self._make_request(
|
|
358
338
|
"GET",
|
|
359
|
-
"/
|
|
339
|
+
"/api/jaeger/v1/nodes",
|
|
360
340
|
params={
|
|
361
341
|
"verbose": "true",
|
|
362
342
|
"attributes": "platforms,scannable_profiles,most_recent_scan",
|
|
@@ -384,11 +364,56 @@ class SicuraAPI:
|
|
|
384
364
|
logger.error(f"Failed to get devices: {e}", exc_info=True)
|
|
385
365
|
return []
|
|
386
366
|
|
|
367
|
+
def create_or_update_control_profile(self, profile_name: str, controls: list[dict]) -> Optional[dict]:
|
|
368
|
+
"""
|
|
369
|
+
Create or update a control profile.
|
|
370
|
+
|
|
371
|
+
:param str profile_name: Name of the control profile
|
|
372
|
+
:param list[dict] controls: List of controls to add to the profile
|
|
373
|
+
:return: The control profile if successfully created or updated, None otherwise
|
|
374
|
+
:rtype: Optional[dict]
|
|
375
|
+
"""
|
|
376
|
+
profile_id = None
|
|
377
|
+
profile_data = None
|
|
378
|
+
params = {"verbose": "true"}
|
|
379
|
+
try:
|
|
380
|
+
# see if the profile already exists
|
|
381
|
+
response = self._make_request("GET", "/api/jaeger/v1/control_profiles", params=params)
|
|
382
|
+
for profile in response:
|
|
383
|
+
if profile["name"] == profile_name:
|
|
384
|
+
profile_id = profile["id"]
|
|
385
|
+
break
|
|
386
|
+
payload = {
|
|
387
|
+
"name": profile_name,
|
|
388
|
+
"description": f"Profile for {profile_name} with {len(controls)} controls.",
|
|
389
|
+
"controls": controls,
|
|
390
|
+
}
|
|
391
|
+
if not profile_id:
|
|
392
|
+
crud_operation = "Created"
|
|
393
|
+
response = self._make_request("POST", "/api/jaeger/v1/control_profiles", data=payload, params=params)
|
|
394
|
+
profile_id = response
|
|
395
|
+
profile_data = self._make_request("GET", f"/api/jaeger/v1/control_profiles/{profile_id}", params=params)
|
|
396
|
+
else:
|
|
397
|
+
crud_operation = "Updated"
|
|
398
|
+
response = self._make_request(
|
|
399
|
+
"PUT", f"/api/jaeger/v1/control_profiles/{profile_id}", data=payload, params=params
|
|
400
|
+
)
|
|
401
|
+
profile_id = response["id"]
|
|
402
|
+
profile_data = response
|
|
403
|
+
logger.info(f"{crud_operation} control profile #{profile_id} in Sicura with {len(controls)} controls.")
|
|
404
|
+
return profile_data
|
|
405
|
+
|
|
406
|
+
return profile_id
|
|
407
|
+
except Exception as e:
|
|
408
|
+
logger.error(f"Failed to create or update control profile: {e}", exc_info=True)
|
|
409
|
+
return None
|
|
410
|
+
|
|
387
411
|
def create_scan_task(
|
|
388
412
|
self,
|
|
389
413
|
device_id: int,
|
|
390
414
|
platform: str,
|
|
391
|
-
profile: SicuraProfile,
|
|
415
|
+
profile: Union[SicuraProfile, str],
|
|
416
|
+
author: Optional[str] = None,
|
|
392
417
|
task_name: Optional[str] = None,
|
|
393
418
|
scheduled_time: Optional[datetime.datetime] = None,
|
|
394
419
|
) -> Optional[str]:
|
|
@@ -398,6 +423,7 @@ class SicuraAPI:
|
|
|
398
423
|
:param int device_id: ID of the device to scan
|
|
399
424
|
:param str platform: Platform name (e.g., 'Red Hat Enterprise Linux 9')
|
|
400
425
|
:param SicuraProfile profile: Scan profile name (e.g., 'I - Mission Critical Classified')
|
|
426
|
+
:param Optional[str] author: Author of the scan task (default: None)
|
|
401
427
|
:param Optional[str] task_name: Name for the scan task (default: auto-generated)
|
|
402
428
|
:param Optional[datetime.datetime] scheduled_time: When to run the scan (default: now)
|
|
403
429
|
:return: Task ID if successful, None otherwise
|
|
@@ -425,7 +451,10 @@ class SicuraAPI:
|
|
|
425
451
|
"scanAttributes": {"platform": platform, "profile": profile},
|
|
426
452
|
}
|
|
427
453
|
|
|
428
|
-
|
|
454
|
+
if author:
|
|
455
|
+
payload["scanAttributes"]["author"] = author
|
|
456
|
+
|
|
457
|
+
result = self._make_request("POST", "/api/jaeger/v1/tasks/", data=payload)
|
|
429
458
|
|
|
430
459
|
if result:
|
|
431
460
|
logger.info(f"Successfully created scan task with ID: {result}")
|
|
@@ -448,7 +477,7 @@ class SicuraAPI:
|
|
|
448
477
|
"""
|
|
449
478
|
try:
|
|
450
479
|
response = self._make_request(
|
|
451
|
-
"GET", "/
|
|
480
|
+
"GET", "/api/jaeger/v1/jobs", params={"verbose": "true", self.FILTER_TASK_ID: task_id}
|
|
452
481
|
)
|
|
453
482
|
|
|
454
483
|
# Handle 404 or empty response
|
|
@@ -483,14 +512,16 @@ class SicuraAPI:
|
|
|
483
512
|
self,
|
|
484
513
|
fqdn: str,
|
|
485
514
|
platform: Optional[str] = None,
|
|
486
|
-
profile: SicuraProfile = SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
515
|
+
profile: Union[SicuraProfile, str] = SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
516
|
+
author: Optional[str] = None,
|
|
487
517
|
) -> Optional[ScanReport]:
|
|
488
518
|
"""
|
|
489
519
|
Get scan results for a specific device.
|
|
490
520
|
|
|
491
521
|
:param str fqdn: Fully qualified domain name of the device
|
|
492
522
|
:param Optional[str] platform: Platform name to filter results (e.g., 'Red Hat Enterprise Linux 9')
|
|
493
|
-
:param SicuraProfile profile: Profile name to filter results
|
|
523
|
+
:param Union[SicuraProfile, str] profile: Profile name to filter results, defaults to I - Mission Critical Classified
|
|
524
|
+
:param Optional[str] author: Author of the scan task (default: None)
|
|
494
525
|
:return: Scan report containing device info and scan results, or None if not found
|
|
495
526
|
:rtype: Optional[ScanReport]
|
|
496
527
|
"""
|
|
@@ -503,7 +534,10 @@ class SicuraAPI:
|
|
|
503
534
|
if profile:
|
|
504
535
|
params["profile"] = profile
|
|
505
536
|
|
|
506
|
-
|
|
537
|
+
if author:
|
|
538
|
+
params["author"] = author
|
|
539
|
+
|
|
540
|
+
response = self._make_request("GET", "/api/jaeger/v1/nodes", params=params)
|
|
507
541
|
|
|
508
542
|
# Handle 404 or empty response
|
|
509
543
|
if not response or (isinstance(response, list) and not response):
|
|
@@ -524,16 +558,17 @@ class SicuraAPI:
|
|
|
524
558
|
return None # Return None if no scans available
|
|
525
559
|
|
|
526
560
|
# Calculate summary stats
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
561
|
+
scan_results = device.get("scans", {}).get("results", [])
|
|
562
|
+
pass_count = sum(1 for scan in scan_results if scan.get("result") == "pass")
|
|
563
|
+
fail_count = sum(1 for scan in scan_results if scan.get("result") == "fail")
|
|
564
|
+
total_count = len(scan_results)
|
|
530
565
|
|
|
531
566
|
# Create the raw result data
|
|
532
567
|
result_data = {
|
|
533
568
|
"device_id": device.get("id"),
|
|
534
569
|
"fqdn": device.get("fqdn"),
|
|
535
570
|
"ip_address": device.get("ip_address"),
|
|
536
|
-
"scans":
|
|
571
|
+
"scans": scan_results,
|
|
537
572
|
"summary": {
|
|
538
573
|
"total": total_count,
|
|
539
574
|
"pass": pass_count,
|
|
@@ -559,7 +594,8 @@ class SicuraAPI:
|
|
|
559
594
|
task_id: Union[int, str],
|
|
560
595
|
fqdn: str,
|
|
561
596
|
platform: Optional[str] = None,
|
|
562
|
-
profile: SicuraProfile = SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
597
|
+
profile: Union[SicuraProfile, str] = SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
598
|
+
author: Optional[str] = None,
|
|
563
599
|
max_wait_time: int = 600,
|
|
564
600
|
poll_interval: int = 10,
|
|
565
601
|
) -> Optional[Union[ScanReport, Dict[str, Any]]]:
|
|
@@ -569,7 +605,8 @@ class SicuraAPI:
|
|
|
569
605
|
:param Union[int, str] task_id: ID of the scan task to monitor
|
|
570
606
|
:param str fqdn: Fully qualified domain name of the device
|
|
571
607
|
:param Optional[str] platform: Platform name to filter results
|
|
572
|
-
:param SicuraProfile profile: Profile name to filter results
|
|
608
|
+
:param Union[SicuraProfile, str] profile: Profile name to filter results, defaults to I - Mission Critical Classified
|
|
609
|
+
:param Optional[str] author: Author of the scan task (default: None)
|
|
573
610
|
:param int max_wait_time: Maximum time to wait in seconds (default: 10 minutes)
|
|
574
611
|
:param int poll_interval: Time between status checks in seconds (default: 10 seconds)
|
|
575
612
|
:return: Scan results once the task is complete, or None if timeout or error
|
|
@@ -601,7 +638,7 @@ class SicuraAPI:
|
|
|
601
638
|
logger.info(f"Scan task {task_id} completed successfully, fetching results...")
|
|
602
639
|
# Wait a moment for results to be processed
|
|
603
640
|
time.sleep(2)
|
|
604
|
-
return self.get_scan_results(fqdn, platform, profile)
|
|
641
|
+
return self.get_scan_results(fqdn=fqdn, platform=platform, profile=profile, author=author)
|
|
605
642
|
else:
|
|
606
643
|
logger.error(f"Scan task {task_id} ended with status {latest_status}")
|
|
607
644
|
return None
|
|
@@ -638,7 +675,7 @@ class SicuraAPI:
|
|
|
638
675
|
if ip_address:
|
|
639
676
|
params[self.FILTER_IP_ADDRESS] = ip_address
|
|
640
677
|
|
|
641
|
-
response = self._make_request("GET", "/
|
|
678
|
+
response = self._make_request("GET", "/api/jaeger/v1/node_templates/", params=params)
|
|
642
679
|
|
|
643
680
|
# Handle 404 or empty response
|
|
644
681
|
if not response or (isinstance(response, dict) and "detail" in response):
|
|
@@ -670,7 +707,7 @@ class SicuraAPI:
|
|
|
670
707
|
# Use PUT method with the correct endpoint and payload
|
|
671
708
|
result = self._make_request(
|
|
672
709
|
"PUT",
|
|
673
|
-
f"/
|
|
710
|
+
f"/api/jaeger/v1/node_templates/{device_id}",
|
|
674
711
|
params={"verbose": "true", "include_controls": "true", "action": "promote"},
|
|
675
712
|
)
|
|
676
713
|
|
|
@@ -697,7 +734,7 @@ class SicuraAPI:
|
|
|
697
734
|
# Use PUT method with the correct endpoint and payload
|
|
698
735
|
result = self._make_request(
|
|
699
736
|
"PUT",
|
|
700
|
-
f"/
|
|
737
|
+
f"/api/jaeger/v1/node_templates/{device_id}",
|
|
701
738
|
params={"verbose": "true", "include_controls": "true", "action": "reject"},
|
|
702
739
|
)
|
|
703
740
|
|
|
@@ -721,7 +758,7 @@ class SicuraAPI:
|
|
|
721
758
|
:rtype: bool
|
|
722
759
|
"""
|
|
723
760
|
try:
|
|
724
|
-
result = self._make_request("DELETE", f"/
|
|
761
|
+
result = self._make_request("DELETE", f"/api/jaeger/v1/nodes/{device_id}")
|
|
725
762
|
|
|
726
763
|
# For DELETE operations, an empty result typically indicates success
|
|
727
764
|
if result is None or result == "" or (isinstance(result, dict) and not result):
|
|
@@ -854,7 +891,7 @@ class SicuraAPI:
|
|
|
854
891
|
}
|
|
855
892
|
|
|
856
893
|
logger.info(f"Creating enforcement task for device {device_id} with {len(ce_names)} CE names")
|
|
857
|
-
result = self._make_request("POST", "/
|
|
894
|
+
result = self._make_request("POST", "/api/jaeger/v1/tasks/", data=payload)
|
|
858
895
|
|
|
859
896
|
if result:
|
|
860
897
|
logger.info(f"Successfully created enforcement task with ID: {result}")
|
|
@@ -46,7 +46,13 @@ def sync_assets(regscale_id: int):
|
|
|
46
46
|
|
|
47
47
|
@sicura.command(name="sync_findings")
|
|
48
48
|
@regscale_id(help="RegScale will create and update findings as children of this record.")
|
|
49
|
-
|
|
49
|
+
@click.option(
|
|
50
|
+
"--trigger_scan",
|
|
51
|
+
"-s",
|
|
52
|
+
is_flag=True,
|
|
53
|
+
help="Trigger a new scan on Sicura assets before syncing.",
|
|
54
|
+
)
|
|
55
|
+
def sync_findings(regscale_id: int, trigger_scan: bool):
|
|
50
56
|
"""
|
|
51
57
|
Sync Sicura findings to RegScale.
|
|
52
58
|
|
|
@@ -60,7 +66,7 @@ def sync_findings(regscale_id: int):
|
|
|
60
66
|
)
|
|
61
67
|
|
|
62
68
|
# Using import_findings method which handles the synchronization
|
|
63
|
-
integration.sync_findings(plan_id=regscale_id)
|
|
69
|
+
integration.sync_findings(plan_id=regscale_id, trigger_scan=trigger_scan)
|
|
64
70
|
|
|
65
71
|
logger.info("[bold green]Finding synchronization complete.")
|
|
66
72
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
RegScale Sicura Integration
|
|
5
5
|
"""
|
|
6
6
|
import datetime
|
|
7
|
-
from typing import Generator, Iterator,
|
|
7
|
+
from typing import Any, Generator, Iterator, Union
|
|
8
8
|
|
|
9
9
|
from regscale.core.utils.date import date_str
|
|
10
10
|
from regscale.integrations.commercial.sicura.api import SicuraAPI, ScanReport, SicuraProfile, Device, ScanResult
|
|
@@ -55,6 +55,8 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
55
55
|
"""
|
|
56
56
|
super().__init__(*args, **kwargs)
|
|
57
57
|
self.api = SicuraAPI()
|
|
58
|
+
self.control_scan = False
|
|
59
|
+
self.control_scan_profile = None
|
|
58
60
|
|
|
59
61
|
def fetch_findings(self, **kwargs) -> Generator[IntegrationFinding, None, None]:
|
|
60
62
|
"""
|
|
@@ -74,6 +76,10 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
74
76
|
logger.warning("No devices found in Sicura")
|
|
75
77
|
return
|
|
76
78
|
|
|
79
|
+
if kwargs.pop("trigger_scan", False):
|
|
80
|
+
logger.info(f"Triggering scans on Sicura {len(devices)} devices...")
|
|
81
|
+
self.trigger_scans(devices)
|
|
82
|
+
|
|
77
83
|
self.num_findings_to_process = 0
|
|
78
84
|
findings_count = 0
|
|
79
85
|
|
|
@@ -99,23 +105,28 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
99
105
|
return
|
|
100
106
|
|
|
101
107
|
# Profiles to scan
|
|
102
|
-
profiles = [SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED
|
|
108
|
+
profiles = [SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED]
|
|
109
|
+
if self.control_scan:
|
|
110
|
+
profiles = [self.control_scan_profile]
|
|
103
111
|
|
|
104
112
|
for profile in profiles:
|
|
105
113
|
yield from self._process_profile_findings(device, profile)
|
|
106
114
|
|
|
107
115
|
def _process_profile_findings(
|
|
108
|
-
self, device: Device, profile: SicuraProfile
|
|
116
|
+
self, device: Device, profile: Union[SicuraProfile, str]
|
|
109
117
|
) -> Generator[IntegrationFinding, None, None]:
|
|
110
118
|
"""
|
|
111
119
|
Process findings for a device with a specific profile
|
|
112
120
|
|
|
113
121
|
:param Device device: The device to process
|
|
114
|
-
:param SicuraProfile profile: The profile to process
|
|
122
|
+
:param Union[SicuraProfile, str] profile: The profile to process
|
|
115
123
|
:yield: IntegrationFinding objects
|
|
116
124
|
:rtype: Generator[IntegrationFinding, None, None]
|
|
117
125
|
"""
|
|
118
|
-
|
|
126
|
+
if self.control_scan and profile == self.control_scan_profile:
|
|
127
|
+
scan_report = self.api.get_scan_results(fqdn=device.fqdn, profile=profile, author="control")
|
|
128
|
+
else:
|
|
129
|
+
scan_report = self.api.get_scan_results(fqdn=device.fqdn, profile=profile)
|
|
119
130
|
|
|
120
131
|
if not scan_report:
|
|
121
132
|
logger.warning(f"No scan results found for device: {device.fqdn} with profile: {profile}")
|
|
@@ -206,7 +217,8 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
206
217
|
mitigation=mitigation,
|
|
207
218
|
)
|
|
208
219
|
|
|
209
|
-
|
|
220
|
+
@staticmethod
|
|
221
|
+
def _extract_scan_data(scan: Any) -> dict:
|
|
210
222
|
"""
|
|
211
223
|
Extract scan data from the scan object
|
|
212
224
|
|
|
@@ -235,7 +247,8 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
235
247
|
"controls": scan.controls if hasattr(scan, "controls") else {},
|
|
236
248
|
}
|
|
237
249
|
|
|
238
|
-
|
|
250
|
+
@staticmethod
|
|
251
|
+
def _extract_control_refs(controls: dict) -> tuple:
|
|
239
252
|
"""
|
|
240
253
|
Extract CCI and SRG references from controls
|
|
241
254
|
|
|
@@ -353,8 +366,9 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
353
366
|
baseline=platform,
|
|
354
367
|
)
|
|
355
368
|
|
|
369
|
+
@staticmethod
|
|
356
370
|
def _create_results_text(
|
|
357
|
-
|
|
371
|
+
title, ce_name, result, description, state, state_reason, cci_ref, srg_refs, is_cci_finding
|
|
358
372
|
) -> str:
|
|
359
373
|
"""
|
|
360
374
|
Create the results text for a finding
|
|
@@ -401,42 +415,6 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
401
415
|
:rtype: Iterator[IntegrationAsset]
|
|
402
416
|
"""
|
|
403
417
|
logger.info("Fetching assets from Sicura...")
|
|
404
|
-
|
|
405
|
-
# Get all devices
|
|
406
|
-
pending_devices = self.api.get_pending_devices()
|
|
407
|
-
for pending_device in pending_devices:
|
|
408
|
-
print(f"Pending device: {pending_device}")
|
|
409
|
-
self.api.accept_pending_device(pending_device.id)
|
|
410
|
-
task_id = self.api.create_scan_task(
|
|
411
|
-
device_id=pending_device.id,
|
|
412
|
-
platform=pending_device.platform,
|
|
413
|
-
profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
414
|
-
)
|
|
415
|
-
if task_id:
|
|
416
|
-
self.api.wait_for_scan_results(
|
|
417
|
-
task_id=task_id,
|
|
418
|
-
fqdn=pending_device.fqdn,
|
|
419
|
-
platform=pending_device.platform,
|
|
420
|
-
profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
421
|
-
)
|
|
422
|
-
else:
|
|
423
|
-
logger.warning(f"Failed to create scan task for device {pending_device.fqdn}")
|
|
424
|
-
continue # Continue to next device if creating scan task failed
|
|
425
|
-
|
|
426
|
-
task_id = self.api.create_scan_task(
|
|
427
|
-
device_id=pending_device.id, platform=pending_device.platform, profile=SicuraProfile.LEVEL_1_SERVER
|
|
428
|
-
)
|
|
429
|
-
if task_id:
|
|
430
|
-
self.api.wait_for_scan_results(
|
|
431
|
-
task_id=task_id,
|
|
432
|
-
fqdn=pending_device.fqdn,
|
|
433
|
-
platform=pending_device.platform,
|
|
434
|
-
profile=SicuraProfile.LEVEL_1_SERVER,
|
|
435
|
-
)
|
|
436
|
-
else:
|
|
437
|
-
logger.warning(f"Failed to create scan task for device {pending_device.fqdn}")
|
|
438
|
-
# No continue needed here as we're at the end of the loop iteration
|
|
439
|
-
|
|
440
418
|
devices = self.api.get_devices()
|
|
441
419
|
|
|
442
420
|
if not devices:
|
|
@@ -479,3 +457,64 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
479
457
|
)
|
|
480
458
|
|
|
481
459
|
self.asset_progress.update(loading_devices, advance=1)
|
|
460
|
+
|
|
461
|
+
def trigger_and_wait_for_scan(self, device: Device) -> None:
|
|
462
|
+
"""
|
|
463
|
+
Trigger a scan and wait for the results
|
|
464
|
+
|
|
465
|
+
:param Device device: The device to trigger a scan for
|
|
466
|
+
:return: None
|
|
467
|
+
"""
|
|
468
|
+
profile = self.control_scan_profile if self.control_scan else SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED
|
|
469
|
+
author = "control" if self.control_scan else None
|
|
470
|
+
task_id = self.api.create_scan_task(
|
|
471
|
+
device_id=device.id,
|
|
472
|
+
platform=device.platforms,
|
|
473
|
+
profile=profile,
|
|
474
|
+
author=author,
|
|
475
|
+
)
|
|
476
|
+
if task_id:
|
|
477
|
+
self.api.wait_for_scan_results(
|
|
478
|
+
task_id=task_id,
|
|
479
|
+
fqdn=device.fqdn,
|
|
480
|
+
platform=device.platforms,
|
|
481
|
+
profile=profile,
|
|
482
|
+
author=author,
|
|
483
|
+
)
|
|
484
|
+
else:
|
|
485
|
+
logger.warning(f"Failed to create scan task for device {device.fqdn}")
|
|
486
|
+
|
|
487
|
+
def trigger_scans(self, devices: list[Device]) -> None:
|
|
488
|
+
"""
|
|
489
|
+
Trigger scans for a list of devices
|
|
490
|
+
|
|
491
|
+
:param list[Device] devices: The devices to trigger scans for
|
|
492
|
+
:return: None
|
|
493
|
+
"""
|
|
494
|
+
# get the SSP's controlImplementations
|
|
495
|
+
if control_imps := regscale_models.ControlImplementation.get_list_by_parent(
|
|
496
|
+
regscale_id=self.plan_id, regscale_module=regscale_models.SecurityPlan.get_module_slug()
|
|
497
|
+
):
|
|
498
|
+
if profile := self.api.create_or_update_control_profile(
|
|
499
|
+
profile_name=f"regscale_ssp_id_{self.plan_id}",
|
|
500
|
+
controls=control_imps,
|
|
501
|
+
):
|
|
502
|
+
self.control_scan = True
|
|
503
|
+
self.control_scan_profile = profile["name"]
|
|
504
|
+
else:
|
|
505
|
+
logger.warning("Failed to create or update control profile")
|
|
506
|
+
self.control_scan = False
|
|
507
|
+
self.control_scan_profile = None
|
|
508
|
+
else:
|
|
509
|
+
self.control_scan = False
|
|
510
|
+
self.control_scan_profile = None
|
|
511
|
+
|
|
512
|
+
if len(devices) > 1:
|
|
513
|
+
from regscale.utils.threading import ThreadManager
|
|
514
|
+
|
|
515
|
+
# use multithreading to trigger scans for multiple devices
|
|
516
|
+
thread_manager = ThreadManager(max_workers=10)
|
|
517
|
+
thread_manager.submit_tasks_from_list(self.trigger_and_wait_for_scan, devices)
|
|
518
|
+
thread_manager.execute_and_verify()
|
|
519
|
+
else:
|
|
520
|
+
self.trigger_and_wait_for_scan(devices[0])
|
|
@@ -82,15 +82,15 @@ class STIGInfo(BaseModel):
|
|
|
82
82
|
"""Data model for STIG Information with optional and required attributes."""
|
|
83
83
|
|
|
84
84
|
version: str
|
|
85
|
-
classification: str
|
|
85
|
+
classification: Optional[str] = None
|
|
86
86
|
customname: Optional[str] = None
|
|
87
87
|
stigid: str
|
|
88
88
|
description: Optional[str] = None
|
|
89
89
|
filename: Optional[str] = None
|
|
90
90
|
releaseinfo: str
|
|
91
91
|
title: str
|
|
92
|
-
uuid: str
|
|
93
|
-
notice: str
|
|
92
|
+
uuid: Optional[str] = None
|
|
93
|
+
notice: Optional[str] = None
|
|
94
94
|
source: Optional[str] = None
|
|
95
95
|
|
|
96
96
|
|
|
@@ -115,10 +115,10 @@ class Vuln(BaseModel):
|
|
|
115
115
|
check_content: Optional[str] = None
|
|
116
116
|
fix_text: str
|
|
117
117
|
check_content_ref: Optional[str] = None
|
|
118
|
-
weight: str
|
|
118
|
+
weight: Optional[str] = None
|
|
119
119
|
stigref: Optional[str] = None
|
|
120
120
|
targetkey: Optional[str] = None
|
|
121
|
-
stig_uuid: str
|
|
121
|
+
stig_uuid: Optional[str] = None
|
|
122
122
|
vuln_discuss: Optional[str] = None
|
|
123
123
|
ia_controls: Optional[str] = None
|
|
124
124
|
class_: Optional[str] = None
|