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
|
@@ -4,6 +4,7 @@ Qualys Total Cloud scanner integration class using JSONLScannerIntegration.
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
+
import threading
|
|
7
8
|
import time
|
|
8
9
|
import traceback
|
|
9
10
|
import xml.etree.ElementTree as ET
|
|
@@ -27,18 +28,20 @@ from regscale.integrations.scanner_integration import (
|
|
|
27
28
|
)
|
|
28
29
|
from regscale.integrations.variables import ScannerVariables
|
|
29
30
|
from regscale.models import AssetStatus, IssueSeverity, IssueStatus
|
|
31
|
+
from regscale import models as regscale_models
|
|
30
32
|
|
|
31
33
|
logger = logging.getLogger("regscale")
|
|
32
34
|
|
|
33
35
|
NO_RESULTS = "No results available"
|
|
34
36
|
NO_DESCRIPTION = "No description available"
|
|
35
37
|
NO_REMEDIATION = "No remediation information available"
|
|
38
|
+
SCANNING_TOOL_NAME = "Qualys Total Cloud"
|
|
36
39
|
|
|
37
40
|
|
|
38
41
|
class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
39
42
|
"""Class for handling Qualys Total Cloud scanner integration using JSONL."""
|
|
40
43
|
|
|
41
|
-
title: str =
|
|
44
|
+
title: str = SCANNING_TOOL_NAME
|
|
42
45
|
asset_identifier_field: str = "qualysId"
|
|
43
46
|
finding_severity_map: Dict[str, Any] = {
|
|
44
47
|
"0": IssueSeverity.NotAssigned.value,
|
|
@@ -73,34 +76,52 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
73
76
|
self.xml_data = kwargs.pop("xml_data", None)
|
|
74
77
|
self.containers = kwargs.pop("containers", None)
|
|
75
78
|
self.is_component = kwargs.get("is_component", False)
|
|
76
|
-
|
|
79
|
+
|
|
80
|
+
self._setup_file_path(kwargs)
|
|
81
|
+
self._apply_vulnerability_creation_setting(kwargs)
|
|
82
|
+
self._apply_ssl_verification_setting(kwargs)
|
|
83
|
+
self._apply_thread_workers_setting(kwargs)
|
|
84
|
+
|
|
85
|
+
super().__init__(*args, **kwargs)
|
|
86
|
+
# No need to initialize clients, they are inherited from the parent class
|
|
87
|
+
|
|
88
|
+
def _setup_file_path(self, kwargs: Dict[str, Any]) -> None:
|
|
89
|
+
"""Setup file path for XML data processing."""
|
|
77
90
|
if self.xml_data and "file_path" not in kwargs:
|
|
78
91
|
kwargs["file_path"] = None
|
|
79
92
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
def _apply_vulnerability_creation_setting(self, kwargs: Dict[str, Any]) -> None:
|
|
94
|
+
"""Apply vulnerability creation setting from variables."""
|
|
95
|
+
if kwargs.get("vulnerability_creation"):
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
if self._has_qualys_vulnerability_creation():
|
|
99
|
+
kwargs["vulnerability_creation"] = QualysVariables.vulnerabilityCreation
|
|
100
|
+
logger.info(f"Using Qualys-specific vulnerability creation mode: {kwargs['vulnerability_creation']}")
|
|
101
|
+
elif self._has_scanner_vulnerability_creation():
|
|
102
|
+
kwargs["vulnerability_creation"] = ScannerVariables.vulnerabilityCreation
|
|
103
|
+
logger.info(f"Using global vulnerability creation mode: {kwargs['vulnerability_creation']}")
|
|
104
|
+
|
|
105
|
+
def _has_qualys_vulnerability_creation(self) -> bool:
|
|
106
|
+
"""Check if QualysVariables has vulnerability creation setting."""
|
|
107
|
+
return hasattr(QualysVariables, "vulnerabilityCreation") and QualysVariables.vulnerabilityCreation
|
|
108
|
+
|
|
109
|
+
def _has_scanner_vulnerability_creation(self) -> bool:
|
|
110
|
+
"""Check if ScannerVariables has vulnerability creation setting."""
|
|
111
|
+
return hasattr(ScannerVariables, "vulnerabilityCreation")
|
|
112
|
+
|
|
113
|
+
def _apply_ssl_verification_setting(self, kwargs: Dict[str, Any]) -> None:
|
|
114
|
+
"""Apply SSL verification setting from ScannerVariables."""
|
|
92
115
|
if not kwargs.get("ssl_verify") and hasattr(ScannerVariables, "sslVerify"):
|
|
93
116
|
kwargs["ssl_verify"] = ScannerVariables.sslVerify
|
|
94
117
|
logger.debug(f"Using SSL verification setting: {kwargs['ssl_verify']}")
|
|
95
118
|
|
|
96
|
-
|
|
119
|
+
def _apply_thread_workers_setting(self, kwargs: Dict[str, Any]) -> None:
|
|
120
|
+
"""Apply thread max workers setting from ScannerVariables."""
|
|
97
121
|
if not kwargs.get("max_workers") and hasattr(ScannerVariables, "threadMaxWorkers"):
|
|
98
122
|
kwargs["max_workers"] = ScannerVariables.threadMaxWorkers
|
|
99
123
|
logger.debug(f"Using thread max workers: {kwargs['max_workers']}")
|
|
100
124
|
|
|
101
|
-
super().__init__(*args, **kwargs)
|
|
102
|
-
# No need to initialize clients, they are inherited from the parent class
|
|
103
|
-
|
|
104
125
|
def is_valid_file(self, data: Any, file_path: Union[Path, str]) -> Tuple[bool, Optional[Dict[str, Any]]]:
|
|
105
126
|
"""
|
|
106
127
|
Check if the XML data is valid for Qualys Total Cloud.
|
|
@@ -230,6 +251,12 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
230
251
|
# Extract host information
|
|
231
252
|
host_info = self._extract_host_information(processed_host)
|
|
232
253
|
|
|
254
|
+
# Log asset creation for debugging
|
|
255
|
+
logger.debug(f"Creating asset for host ID: {host_info['host_id']}")
|
|
256
|
+
logger.debug(f"Asset name: {host_info['name']}")
|
|
257
|
+
logger.debug(f"Plan ID: {self.plan_id}, Parent Module: {self.parent_module}")
|
|
258
|
+
logger.debug(f"Is Component: {self.is_component}")
|
|
259
|
+
|
|
233
260
|
# Create and return the asset
|
|
234
261
|
return IntegrationAsset(
|
|
235
262
|
name=host_info["name"],
|
|
@@ -448,6 +475,23 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
448
475
|
|
|
449
476
|
current_time = self.scan_date or get_current_datetime()
|
|
450
477
|
|
|
478
|
+
# Extract CVSS scores and convert to float if possible
|
|
479
|
+
cvss_v3_score = detection.get("CVSS3_BASE")
|
|
480
|
+
cvss_v2_score = detection.get("CVSS_BASE")
|
|
481
|
+
|
|
482
|
+
# Convert CVSS scores to float if they're strings
|
|
483
|
+
if cvss_v3_score and isinstance(cvss_v3_score, str):
|
|
484
|
+
try:
|
|
485
|
+
cvss_v3_score = float(cvss_v3_score)
|
|
486
|
+
except (ValueError, TypeError):
|
|
487
|
+
cvss_v3_score = None
|
|
488
|
+
|
|
489
|
+
if cvss_v2_score and isinstance(cvss_v2_score, str):
|
|
490
|
+
try:
|
|
491
|
+
cvss_v2_score = float(cvss_v2_score)
|
|
492
|
+
except (ValueError, TypeError):
|
|
493
|
+
cvss_v2_score = None
|
|
494
|
+
|
|
451
495
|
return {
|
|
452
496
|
"qid": detection.get("QID", "Unknown"),
|
|
453
497
|
"severity": detection.get("SEVERITY", "0"),
|
|
@@ -456,9 +500,9 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
456
500
|
"last_found": detection.get("LAST_FOUND_DATETIME", current_time),
|
|
457
501
|
"unique_id": detection.get("UNIQUE_VULN_ID", f"QID-{detection.get('QID', 'Unknown')}"),
|
|
458
502
|
"results": detection.get("RESULTS", NO_RESULTS),
|
|
459
|
-
"cvss_v3_score":
|
|
503
|
+
"cvss_v3_score": cvss_v3_score,
|
|
460
504
|
"cvss_v3_vector": detection.get("CVSS3_VECTOR", ""),
|
|
461
|
-
"cvss_v2_score":
|
|
505
|
+
"cvss_v2_score": cvss_v2_score,
|
|
462
506
|
"cvss_v2_vector": detection.get("CVSS_VECTOR", ""),
|
|
463
507
|
}
|
|
464
508
|
|
|
@@ -504,29 +548,79 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
504
548
|
if not detection:
|
|
505
549
|
return ""
|
|
506
550
|
|
|
507
|
-
cve_id = ""
|
|
508
551
|
try:
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
if isinstance(cve_data, list) and cve_data:
|
|
520
|
-
cve_id = str(cve_data[0]) if cve_data[0] else ""
|
|
521
|
-
elif isinstance(cve_data, str):
|
|
522
|
-
cve_id = cve_data
|
|
523
|
-
elif cve_data:
|
|
524
|
-
# Try to convert to string if it's something else
|
|
525
|
-
cve_id = str(cve_data)
|
|
552
|
+
# Try to extract CVE from CVE_ID_LIST first
|
|
553
|
+
cve_id = self._extract_cve_from_cve_list(detection)
|
|
554
|
+
if cve_id:
|
|
555
|
+
return cve_id
|
|
556
|
+
|
|
557
|
+
# Try direct CVE fields if CVE_ID_LIST didn't work
|
|
558
|
+
cve_id = self._extract_cve_from_direct_fields(detection)
|
|
559
|
+
if cve_id:
|
|
560
|
+
return cve_id
|
|
561
|
+
|
|
526
562
|
except Exception as e:
|
|
527
563
|
logger.warning(f"Error extracting CVE_ID: {str(e)}")
|
|
528
564
|
|
|
529
|
-
return
|
|
565
|
+
return ""
|
|
566
|
+
|
|
567
|
+
def _extract_cve_from_cve_list(self, detection: Dict[str, Any]) -> str:
|
|
568
|
+
"""
|
|
569
|
+
Extract CVE ID from CVE_ID_LIST field.
|
|
570
|
+
|
|
571
|
+
:param Dict[str, Any] detection: Detection data
|
|
572
|
+
:return: CVE ID string
|
|
573
|
+
:rtype: str
|
|
574
|
+
"""
|
|
575
|
+
cve_list = detection.get("CVE_ID_LIST", {})
|
|
576
|
+
if not cve_list:
|
|
577
|
+
return ""
|
|
578
|
+
|
|
579
|
+
if not isinstance(cve_list, dict):
|
|
580
|
+
logger.warning(f"Expected dictionary for CVE_ID_LIST, got {type(cve_list)}")
|
|
581
|
+
return ""
|
|
582
|
+
|
|
583
|
+
if "CVE_ID" not in cve_list:
|
|
584
|
+
return ""
|
|
585
|
+
|
|
586
|
+
cve_data = cve_list.get("CVE_ID", [])
|
|
587
|
+
return self._convert_cve_data_to_string(cve_data)
|
|
588
|
+
|
|
589
|
+
def _extract_cve_from_direct_fields(self, detection: Dict[str, Any]) -> str:
|
|
590
|
+
"""
|
|
591
|
+
Extract CVE ID from direct CVE fields.
|
|
592
|
+
|
|
593
|
+
:param Dict[str, Any] detection: Detection data
|
|
594
|
+
:return: CVE ID string
|
|
595
|
+
:rtype: str
|
|
596
|
+
"""
|
|
597
|
+
# Try CVE field directly
|
|
598
|
+
cve_id = detection.get("CVE", "")
|
|
599
|
+
if cve_id:
|
|
600
|
+
return str(cve_id)
|
|
601
|
+
|
|
602
|
+
# Try CVE_ID field directly
|
|
603
|
+
cve_id = detection.get("CVE_ID", "")
|
|
604
|
+
if cve_id:
|
|
605
|
+
return str(cve_id)
|
|
606
|
+
|
|
607
|
+
return ""
|
|
608
|
+
|
|
609
|
+
def _convert_cve_data_to_string(self, cve_data: Any) -> str:
|
|
610
|
+
"""
|
|
611
|
+
Convert CVE data to string format.
|
|
612
|
+
|
|
613
|
+
:param Any cve_data: CVE data to convert
|
|
614
|
+
:return: CVE ID string
|
|
615
|
+
:rtype: str
|
|
616
|
+
"""
|
|
617
|
+
if isinstance(cve_data, list) and cve_data:
|
|
618
|
+
return str(cve_data[0]) if cve_data[0] else ""
|
|
619
|
+
elif isinstance(cve_data, str):
|
|
620
|
+
return cve_data
|
|
621
|
+
elif cve_data:
|
|
622
|
+
return str(cve_data)
|
|
623
|
+
return ""
|
|
530
624
|
|
|
531
625
|
def _extract_cve_id_from_xml(self, detection: Optional[Union[Dict[str, Any], ET.Element]]) -> str:
|
|
532
626
|
"""
|
|
@@ -648,6 +742,12 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
648
742
|
current_time = self.scan_date or get_current_datetime()
|
|
649
743
|
qid = finding_data.get("qid", "Unknown")
|
|
650
744
|
|
|
745
|
+
# Log the finding data for debugging
|
|
746
|
+
logger.debug(f"Creating finding for QID {qid}, host {host_id}")
|
|
747
|
+
logger.debug(f"CVE: {finding_data.get('cve_id', 'None')}")
|
|
748
|
+
logger.debug(f"CVSS V3 Score: {finding_data.get('cvss_v3_score', 'None')}")
|
|
749
|
+
logger.debug(f"CVSS V2 Score: {finding_data.get('cvss_v2_score', 'None')}")
|
|
750
|
+
|
|
651
751
|
return IntegrationFinding(
|
|
652
752
|
title=finding_data.get("title", f"Qualys Vulnerability QID-{qid}"),
|
|
653
753
|
description=finding_data.get("diagnosis", NO_DESCRIPTION),
|
|
@@ -1376,3 +1476,169 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
1376
1476
|
|
|
1377
1477
|
# Default to Open for any unknown status
|
|
1378
1478
|
return IssueStatus.Open
|
|
1479
|
+
|
|
1480
|
+
def create_vulnerability_from_finding(
|
|
1481
|
+
self, finding: IntegrationFinding, asset: regscale_models.Asset, scan_history: regscale_models.ScanHistory
|
|
1482
|
+
) -> regscale_models.Vulnerability:
|
|
1483
|
+
"""
|
|
1484
|
+
Override the parent method to add better debugging and ensure proper vulnerability mapping creation.
|
|
1485
|
+
|
|
1486
|
+
:param IntegrationFinding finding: The integration finding
|
|
1487
|
+
:param regscale_models.Asset asset: The associated asset
|
|
1488
|
+
:param regscale_models.ScanHistory scan_history: The scan history
|
|
1489
|
+
:return: The created vulnerability
|
|
1490
|
+
:rtype: regscale_models.Vulnerability
|
|
1491
|
+
"""
|
|
1492
|
+
logger.debug(f"Creating vulnerability from finding: {finding.title}")
|
|
1493
|
+
logger.debug(f"Asset ID: {asset.id}, Asset Name: {asset.name}")
|
|
1494
|
+
logger.debug(f"Scan History ID: {scan_history.id}")
|
|
1495
|
+
logger.debug(f"Plan ID: {self.plan_id}, Parent Module: {self.parent_module}")
|
|
1496
|
+
logger.debug(f"Is Component: {self.is_component}")
|
|
1497
|
+
|
|
1498
|
+
# Call the parent method
|
|
1499
|
+
vulnerability = super().create_vulnerability_from_finding(finding, asset, scan_history)
|
|
1500
|
+
|
|
1501
|
+
logger.debug(f"Created vulnerability with ID: {vulnerability.id}")
|
|
1502
|
+
logger.debug(f"Vulnerability parentId: {vulnerability.parentId}")
|
|
1503
|
+
logger.debug(f"Vulnerability parentModule: {vulnerability.parentModule}")
|
|
1504
|
+
|
|
1505
|
+
# Verify the vulnerability mapping was created
|
|
1506
|
+
try:
|
|
1507
|
+
mappings = regscale_models.VulnerabilityMapping.find_by_vulnerability(vulnerability.id)
|
|
1508
|
+
logger.debug(f"Found {len(mappings)} vulnerability mappings for vulnerability {vulnerability.id}")
|
|
1509
|
+
for mapping in mappings:
|
|
1510
|
+
logger.debug(
|
|
1511
|
+
f"Mapping - Asset ID: {mapping.assetId}, Scan ID: {mapping.scanId}, Security Plan ID: {mapping.securityPlanId}"
|
|
1512
|
+
)
|
|
1513
|
+
except Exception as e:
|
|
1514
|
+
logger.warning(f"Error checking vulnerability mappings: {e}")
|
|
1515
|
+
|
|
1516
|
+
return vulnerability
|
|
1517
|
+
|
|
1518
|
+
def handle_vulnerability(
|
|
1519
|
+
self,
|
|
1520
|
+
finding: IntegrationFinding,
|
|
1521
|
+
asset: Optional[regscale_models.Asset],
|
|
1522
|
+
scan_history: regscale_models.ScanHistory,
|
|
1523
|
+
) -> Optional[int]:
|
|
1524
|
+
"""
|
|
1525
|
+
Override parent method to ensure Qualys findings always create vulnerabilities.
|
|
1526
|
+
This ensures that Qualys vulnerabilities are properly populated in RegScale.
|
|
1527
|
+
|
|
1528
|
+
:param IntegrationFinding finding: The integration finding
|
|
1529
|
+
:param Optional[regscale_models.Asset] asset: The associated asset
|
|
1530
|
+
:param regscale_models.ScanHistory scan_history: The scan history
|
|
1531
|
+
:rtype: Optional[int]
|
|
1532
|
+
:return: The vulnerability ID
|
|
1533
|
+
"""
|
|
1534
|
+
# Check for required fields - either plugin_name or cve must be present
|
|
1535
|
+
if not (finding.plugin_name or finding.cve):
|
|
1536
|
+
logger.warning(
|
|
1537
|
+
f"Qualys: Skipping vulnerability creation - missing plugin_name and cve for finding {finding.external_id}"
|
|
1538
|
+
)
|
|
1539
|
+
return None
|
|
1540
|
+
|
|
1541
|
+
# Ensure vulnerability creation is enabled for Qualys
|
|
1542
|
+
logger.debug(f"Qualys: Vulnerability creation setting: {self.vulnerability_creation}")
|
|
1543
|
+
if self.vulnerability_creation == "NoIssue":
|
|
1544
|
+
logger.debug(f"Qualys: Vulnerability creation disabled, skipping finding {finding.external_id}")
|
|
1545
|
+
return None
|
|
1546
|
+
|
|
1547
|
+
# Create vulnerability using parent method
|
|
1548
|
+
logger.debug(f"Qualys: Calling parent handle_vulnerability for finding {finding.external_id}")
|
|
1549
|
+
vulnerability_id = super().handle_vulnerability(finding, asset, scan_history)
|
|
1550
|
+
|
|
1551
|
+
if vulnerability_id:
|
|
1552
|
+
logger.debug(f"Qualys: Created vulnerability {vulnerability_id} for finding {finding.external_id}")
|
|
1553
|
+
else:
|
|
1554
|
+
logger.warning(f"Qualys: Failed to create vulnerability for finding {finding.external_id}")
|
|
1555
|
+
|
|
1556
|
+
return vulnerability_id
|
|
1557
|
+
|
|
1558
|
+
def set_severity_count_for_scan(
|
|
1559
|
+
self, severity: str, scan_history: regscale_models.ScanHistory, lock: Optional[threading.RLock] = None
|
|
1560
|
+
) -> None:
|
|
1561
|
+
"""
|
|
1562
|
+
Override parent method to ensure Qualys scan history severity counts are properly updated.
|
|
1563
|
+
This ensures that the vulnerability counts are accurately reflected in the scan history.
|
|
1564
|
+
|
|
1565
|
+
:param str severity: Severity of the vulnerability
|
|
1566
|
+
:param regscale_models.ScanHistory scan_history: Scan history object
|
|
1567
|
+
:param Optional[threading.RLock] lock: Thread lock for synchronization
|
|
1568
|
+
:rtype: None
|
|
1569
|
+
"""
|
|
1570
|
+
# Use parent method to update severity counts with thread-safe locking
|
|
1571
|
+
# Pass lock if provided, otherwise use our instance lock
|
|
1572
|
+
super().set_severity_count_for_scan(severity, scan_history, lock or self.scan_history_lock)
|
|
1573
|
+
|
|
1574
|
+
def create_scan_history(self) -> regscale_models.ScanHistory:
|
|
1575
|
+
"""
|
|
1576
|
+
Override parent method to ensure Qualys scan history is properly created.
|
|
1577
|
+
This ensures that the scanning tool name is correctly set for Qualys scans.
|
|
1578
|
+
Also reuses existing scan history records for the same day and tool to avoid duplicates.
|
|
1579
|
+
|
|
1580
|
+
:return: A newly created or reused ScanHistory object
|
|
1581
|
+
:rtype: regscale_models.ScanHistory
|
|
1582
|
+
"""
|
|
1583
|
+
logger.debug(f"Creating scan history for plan {self.plan_id}, module {self.parent_module}")
|
|
1584
|
+
|
|
1585
|
+
try:
|
|
1586
|
+
# Load existing scans for the plan/module
|
|
1587
|
+
existing_scans = regscale_models.ScanHistory.get_all_by_parent(
|
|
1588
|
+
parent_id=self.plan_id, parent_module=self.parent_module
|
|
1589
|
+
)
|
|
1590
|
+
|
|
1591
|
+
# Normalize target date to date component only
|
|
1592
|
+
target_dt = self.scan_date if self.scan_date else get_current_datetime()
|
|
1593
|
+
target_date_only = target_dt.split("T")[0] if isinstance(target_dt, str) else str(target_dt)[:10]
|
|
1594
|
+
|
|
1595
|
+
# Find an existing scan for today and this tool
|
|
1596
|
+
for scan in existing_scans:
|
|
1597
|
+
try:
|
|
1598
|
+
if getattr(scan, "scanningTool", None) == SCANNING_TOOL_NAME and getattr(scan, "scanDate", None):
|
|
1599
|
+
scan_date = str(scan.scanDate)
|
|
1600
|
+
scan_date_only = scan_date.split("T")[0]
|
|
1601
|
+
if scan_date_only == target_date_only:
|
|
1602
|
+
# Reuse this scan history; refresh last updated
|
|
1603
|
+
logger.debug(f"Reusing existing scan history {scan.id} for {target_date_only}")
|
|
1604
|
+
scan.dateLastUpdated = get_current_datetime()
|
|
1605
|
+
scan.lastUpdatedById = self.assessor_id
|
|
1606
|
+
scan.save()
|
|
1607
|
+
return scan
|
|
1608
|
+
except Exception:
|
|
1609
|
+
# Skip any malformed scan records
|
|
1610
|
+
continue
|
|
1611
|
+
|
|
1612
|
+
# No existing same-day scan found, create new
|
|
1613
|
+
logger.debug("No existing scan history found for today, creating new one")
|
|
1614
|
+
scan_history = regscale_models.ScanHistory(
|
|
1615
|
+
parentId=self.plan_id,
|
|
1616
|
+
parentModule=self.parent_module,
|
|
1617
|
+
scanningTool=SCANNING_TOOL_NAME, # Ensure proper scanning tool name
|
|
1618
|
+
scanDate=self.scan_date if self.scan_date else get_current_datetime(),
|
|
1619
|
+
createdById=self.assessor_id,
|
|
1620
|
+
lastUpdatedById=self.assessor_id,
|
|
1621
|
+
tenantsId=self.tenant_id,
|
|
1622
|
+
vLow=0,
|
|
1623
|
+
vMedium=0,
|
|
1624
|
+
vHigh=0,
|
|
1625
|
+
vCritical=0,
|
|
1626
|
+
).create()
|
|
1627
|
+
|
|
1628
|
+
logger.debug(f"Created new scan history with ID: {scan_history.id}")
|
|
1629
|
+
|
|
1630
|
+
# Ensure the scan history is properly created and cached
|
|
1631
|
+
count = 0
|
|
1632
|
+
regscale_models.ScanHistory.delete_object_cache(scan_history)
|
|
1633
|
+
while not regscale_models.ScanHistory.get_object(object_id=scan_history.id) or count > 10:
|
|
1634
|
+
logger.info("Waiting for ScanHistory to be created...")
|
|
1635
|
+
time.sleep(1)
|
|
1636
|
+
count += 1
|
|
1637
|
+
regscale_models.ScanHistory.delete_object_cache(scan_history)
|
|
1638
|
+
|
|
1639
|
+
return scan_history
|
|
1640
|
+
|
|
1641
|
+
except Exception as e:
|
|
1642
|
+
logger.error(f"Error in create_scan_history: {e}")
|
|
1643
|
+
# Fallback: create new scan history using parent method
|
|
1644
|
+
return super().create_scan_history()
|